# Promotion, Multiple Dispatch, in example using temperature

**URL:** <https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843>\
**Category:** New to Julia\
**Tags:** question, dispatch, unitful\
**Created:** [November 26, 2020, 11:57pm UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843 "2020-11-26T23:57:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![nero57](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nero57/32/19204_2.png) [@nero57](https://discourse.julialang.org/u/nero57)\
**Post date:** [November 26, 2020, 11:57pm UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/1 "2020-11-26T23:57:06Z")

</div>

Hello all,

I’m trying to understand conversion, multiple dispatch, and promotion using a modified version of the example described in this [blogpost](https://erik-engheim.medium.com/defining-custom-units-in-julia-and-python-513c34a4c971). The post discusses multiple dispatch, promotion, and conversion using the example of temperature, and how to combine measurements in different temperature units (Kelvin, Celsius, Fahrenheit).

Here is a modified collection of the script used in this post (Julia 1.5):

```julia
abstract type Temperature end
import Base:+,-, promote,promote_rule
types = [:Celsius, :Kelvin, :Fahrenheit]
for T in types
    @eval begin
        struct $T <: Temperature
            value::Float64
        end

        +(x::$T, y::$T) = $T(x.value + y.value)
        -(x::$T, y::$T) = $T(x.value - y.value)
    end
end
convert(::Type{Kelvin}, t::Celsius) = Kelvin(t.value + 273.15)
convert(::Type{Kelvin}, t::Fahrenheit) = Kelvin(Celsius(t))
convert(::Type{Celsius}, t::Kelvin) = Celsius(t.value - 273.15)
convert(::Type{Celsius}, t::Fahrenheit) = Celsius((t.value - 32)*5/9)
convert(::Type{Fahrenheit}, t::Celsius) = Fahrenheit(t.value*9/5 + 32)
convert(::Type{Fahrenheit}, t::Kelvin) = Fahrenheit(Ceslius(t))
for T in types, S in types
    if S != T
       @eval $T(temp::$S) = convert($T, temp) 
    end
end

promote_rule(::Type{Kelvin}, ::Type{Celsius}) = Kelvin
promote_rule(::Type{Fahrenheit}, ::Type{Kelvin}) = Kelvin
promote_rule(::Type{Fahrenheit}, ::Type{Celsius}) = Celsius

+(x::Temperature, y::Temperature) = +(promote(x,y)...);
-(x::Temperature, y::Temperature) = -(promote(x,y)...);

```

From what I understand of this based on the [docs](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/), ~~calling ` promote_type(Kelvin,Celsius)` Should return `Kelvin`, but instead it’s returning `Temperature` in the REPL. Moreover,~~ attempting to add, say `Kelvin(4)+Celsius(5)` returns an error that there is no method for adding these two types. All this leads me to conclude that the promotion text here is not working as intended. If so,could anyone help me see why that is not the case?

Edit: using

```julia
import Base: promote,promote_rule

```

Fixes the behavior before: now `promote_type(Kelvin,Celsius)` returns `Kelvin`, as expected. However, adding between types still seems to be failing, so not sure what’s going on.  
In the REPL, I get

```julia
 Kelvin(4)+Celsius(5)
ERROR: MethodError: Cannot `convert` an object of type Celsius to an object of type Kelvin
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:171
  Kelvin(::Celsius) at types_temp.jl:23
  Kelvin(::Any) at types_temp.jl:8
Stacktrace:
 [1] _promote at .\promotion.jl:259 [inlined]
 [2] promote at .\promotion.jl:282 [inlined]
 [3] +(::Kelvin, ::Celsius) at types_temp.jl:33
 [4] top-level scope at REPL[1]:1

```

~~Any help?~~

Edit 2: Okay, the obvious solution is that I also needed `import Base: convert`. Which fixed everything and makes the code run in the expected way.

I guess I solved all of my problems, but I guess my question is now:

is this best practice for writing Julia code (as artificial as this example might be)? Is there a more elegant, more Julian way to do the same thing? Why the need to import all of these Base functions? What negative behavior occurs if that import feature is implicit?

---

<div class="post-metadata">

**Author:** ![apo383](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/apo383/32/11272_2.png) [@apo383](https://discourse.julialang.org/u/apo383)\
**Post date:** [November 27, 2020, 2:01am UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/2 "2020-11-27T02:01:48Z")

</div>

I can speculate on a couple reasons to only import explicitly. First, just like `using`, there may be some significant compilation involved when using an external package. I just tried `import DifferentialEquations.solve` and it triggered a lengthy precompilation. Second, it is just good programming practice to warn about imports. Otherwise you might forget that somewhere deep in your module, you accidentally did some type piracy or damaging redefinition of another package’s code.

---

<div class="post-metadata">

**Author:** ![josePereiro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/josepereiro/32/17322_2.png) [@josePereiro](https://discourse.julialang.org/u/josePereiro)\
**Post date:** [November 27, 2020, 3:30am UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/3 "2020-11-27T03:30:10Z")

</div>

> [@apo383](#):
>
> it is just good programming practice to warn about imports. Otherwise you might forget that somewhere deep in your module, you accidentally did some type piracy or damaging redefinition of another package’s code.

I think this is the main reason. You should explicitly import or qualify the function (from other package) you want to extend as a prove that you know what your doing.

---

<div class="post-metadata">

**Author:** ![briochemc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/briochemc/32/4209_2.png) [@briochemc](https://discourse.julialang.org/u/briochemc)\
**Post date:** [November 27, 2020, 4:02am UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/4 "2020-11-27T04:02:02Z")

</div>

Just FWIW, for whoever wanting to do temperature conversions and _not_ thinking about the multiple-dispatch and promotion mechanisms, you can use [Unitful.jl](https://github.com/PainterQubits/Unitful.jl/blob/master/docs/src/temperature.md):

```julia
julia> using Unitful: K, °F, °C

julia> 3.2K |> °F
-453.91 °F

julia> 102.3°F |> °C
39.0555555555556 °C

julia> 102K + 10.0°C
385.15 K

```

---

<div class="post-metadata">

**Author:** ![FedericoStra](https://avatars.discourse-cdn.com/v4/letter/f/76d3ee/32.png) [@FedericoStra](https://discourse.julialang.org/u/FedericoStra)\
**Post date:** [November 27, 2020, 1:30pm UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/5 "2020-11-27T13:30:26Z")

</div>

Moreover, `Unitful` prevents erroneous operations between affine quantities such as `0°C + 0°C`. On the other hand the code above has

```julia
Celsius(0) + Celsius(0) == Celsius(0)

```

but

```julia
Celsius(Kelvin(Celsius(0)) + Kelvin(Celsius(0))) == Celsius(273.15)

```

which can cause subtle bugs, because the meaning of `+` changes depending on which units one is currently working in.  
20°C is not the “double” of 10°C, it is 293.15°C: `2K(10.0°C) |> °C`.

---

<div class="post-metadata">

**Author:** ![nero57](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nero57/32/19204_2.png) [@nero57](https://discourse.julialang.org/u/nero57)\
**Post date:** [November 27, 2020, 5:23pm UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/6 "2020-11-27T17:23:10Z")

</div>

```julia
Celsius(Kelvin(Celsius(0)) + Kelvin(Celsius(0))) == Celsius(273.15)

```

Oh that is a problem, oops!

Thanks for the answers everyone, very helpful. This example was more for me to better understand how the features of julia, obviously it’s not perfect but I do think I understand these concepts better.

---

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [November 28, 2020, 12:27am UTC](https://discourse.julialang.org/t/promotion-multiple-dispatch-in-example-using-temperature/50843/7 "2020-11-28T00:27:46Z")

</div>

If you muck around with Temperature, you need to know there is two types of Temperature Types.

1. Temperature
2. TemperatureDifference

These are NOT the same.

```julia
for example:  
                      0 C == 273 K in Temperature
BUT
                      0 C == 0 K in TemperatureDifference

```

If you get these two types mixed up, you will have ENDLESS problems in your coding.
