# Type promotion not working as expected

**URL:** https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944
**Category:** New to Julia
**Tags:** question, type
**Created:** [August 6, 2021, 1:44pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944 "2021-08-06T13:44:33Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![AronT](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aront/32/24890_2.png) [@AronT](https://discourse.julialang.org/u/AronT)
#### Post date: [August 6, 2021, 1:44pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/1 "2021-08-06T13:44:33Z")

</div>

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](https://github.com/FourMInfo/JuliaforBeginners) 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:

```julia
abstract type Angle end

struct Radian <: Angle
    radians::Float64
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)

    +(Θ::Radian, α::Radian) = Radian(Θ.radians + α.radians)
    -(Θ::Radian, α::Radian) = Radian(Θ.radians - α.radians)

    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

    function show(io::IO, rad::Radian)
    print(io, rad.radians, "rad")
    end

    # convert constructors
    Radian(dms::DMS) = Radian(deg2rad(dms.seconds/3600))
    DMS(rad::Radian) = DMS(floor(Int, rad2deg(rad.radians) * 3600))

    convert(::Type{Radian}, dms::DMS) = Radian(dms)
    convert(::Type{DMS}, rad::Radian) = DMS(rad)

    sin(rad::Radian) = Base.sin(rad.radians)
    cos(rad::Radian) = Base.cos(rad.radians)

    sin(dms::DMS) = sin(Radian(dms))
    cos(dms::DMS) = cos(Radian(dms))

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

    *(coeff::Number, rad::Radian) = Radian(coeff * rad.radians)
    *(rad::Radian, coeff::Number) = coeff * rad
    /(rad::Radian, denom::Number) = Radian(rad.radians/denom)

    const ° = Degree(1)
    const rad = Radian(1)

    Base.promote_rule(::Type{Radian}, ::Type{DMS}) = Radian
end

```

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

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

```

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:

```julia
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
  +(::Radian, ::Radian) at /Users/aronet/Code/FourM/Study/Julia/JuliaforBeginners/angleunits.jl:28
Stacktrace:
 [1] top-level scope
   @ REPL[5]:1

```

Thanks for any help.

---

<div class="post-metadata">

### Author: ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)
#### Post date: [August 6, 2021, 1:46pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/2 "2021-08-06T13:46:55Z")

</div>

> [@AronT](#):
>
> `abstract type Angle end`

`abstract type Angle <: Number end`

reason:

> <https://github.com/JuliaLang/julia/blob/d1145d4569c4f3efcb91e99d35140d63d8613870/base/promotion.jl#L340-L343>

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [August 6, 2021, 1:50pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/3 "2021-08-06T13:50:43Z")

</div>

Alternatively, define methods

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

```

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

---

<div class="post-metadata">

### Author: ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)
#### Post date: [August 6, 2021, 1:51pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/4 "2021-08-06T13:51:06Z")

</div>

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

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [August 6, 2021, 1:52pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/5 "2021-08-06T13:52:54Z")

</div>

> [@jling](#):
>
> this would be going in the other direction…

Yes, either inherit them, or do it yourself.

---

<div class="post-metadata">

### Author: ![AronT](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aront/32/24890_2.png) [@AronT](https://discourse.julialang.org/u/AronT)
#### Post date: [August 6, 2021, 1:53pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/6 "2021-08-06T13:53:51Z")

</div>

@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.

---

<div class="post-metadata">

### Author: ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)
#### Post date: [August 6, 2021, 2:19pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/7 "2021-08-06T14:19:49Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)
#### Post date: [August 6, 2021, 2:22pm UTC](https://discourse.julialang.org/t/type-promotion-not-working-as-expected/65944/8 "2021-08-06T14:22:26Z")

</div>

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.
