# Type promotion not working as expected

So I am learning Julia from Erik Engheim’s book “Julia for Beginners”. I am working through the code as I read the book (I put all my work in Github if anyone else is using the book and want to compare their own code)

He has a chapter on defining angle types as a way to teach type conversion rules and promotion (which is quite a nice feature of Julia). Unfortunately, my code isn’t working as expected and I’m not sure why. Here is the code:

``````abstract type Angle end

end

struct DMS <: Angle
seconds::Int
end

begin
Degree(degrees::Integer) = Minute(degrees * 60)
Degree(deg::Integer, min::Integer) = Degree(deg) + Minute(min)
Degree(deg::Integer, min::Integer, secs::Integer) = Degree(deg, min) + Second(secs)

function Minute(minutes::Integer)
DMS(minutes * 60)
end

function Second(seconds::Integer)
DMS(seconds)
end

import Base: -, +, show, convert, *, /, promote_rule
+(Θ::DMS, α::DMS) = DMS(Θ.seconds + α.seconds)
-(Θ::DMS, α::DMS) = DMS(Θ.seconds - α.seconds)

function degrees(dms::DMS)
minutes = dms.seconds ÷ 60
minutes ÷ 60
end

function minutes(dms::DMS)
minutes = dms.seconds ÷ 60
minutes % 60
end

seconds(dms::DMS) = dms.seconds % 60

function show(io::IO, dms::DMS)
print(io, degrees(dms), "° ", minutes(dms), "' ", seconds(dms), "''")
end

end

# convert constructors

*(coeff::Number, dms::DMS) = DMS(coeff * dms.seconds)
*(dms::DMS, coeff::Number) = coeff * dms
/(dms::DMS, denom::Number) = DMS(dms.seconds/denom)

const ° = Degree(1)

end
``````

As can be seen I have a promote rule and this works for me:

``````+(promote(90°,3.14rad/2)...)
# result:
``````

But it I just do a `+` without the explicit `promote` it complains. I don’t understand why it isn’t automatically promoting the DMS to Radian:

``````90°+ 3.14rad/2
# result:
ERROR: MethodError: no method matching +(::DMS, ::Radian)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
+(::DMS, ::DMS) at /Users/aronet/Code/FourM/Study/Julia/JuliaforBeginners/angleunits.jl:25
Stacktrace:
[1] top-level scope
@ REPL[5]:1
``````

Thanks for any help.

`abstract type Angle <: Number end`

reason:

Alternatively, define methods

``````+(x::Angle, y::Angle) = +(promote(x,y)...)
# etc.
``````

`Number` already has these defined, so `Angle` inherits them.

if the point is to get promotion rules to do the work for you, this would be going in the other direction…

Yes, either inherit them, or do it yourself.

@jling Thanks so much! That solved the problem!

For those reading the book, I am not sure why Engheim left this out in his definition but it seems he might have preferred @DNF’s solution since he is trying to get you to “do the work yourself”. Of course he left that bit out, which was confusing. Thanks @DNF for your insights as well.

That makes sense. One thing to note is that although Julia is flexible enough to let you write your own promote rules, doing so correctly is surprisingly difficult, and result in infinite recursion. As such for most real use cases, you probably want to just use the ones in Base since they probably do what you want and are relatively reliable.

2 Likes

my advise is: if they are really number-like thing, user promotion to make life easy, other-wise, don’t. For example, you should never use promotion rule for adding vector to a scalar.

3 Likes