Hello all,
I’m trying to understand conversion, multiple dispatch, and promotion using a modified version of the example described in this blogpost. 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):
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, calling attempting to add, say promote_type(Kelvin,Celsius)
Should return Kelvin
, but instead it’s returning Temperature
in the REPL. Moreover,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
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
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?