Understanding how `promotion` works: promote Celsius to Kelvin

I am trying to follow this blog which shows how to create a unit conversion code.

I have tried to define two temperate types Celsius and Kelvin and I define a promotion rule where Celsius get promoted to Kelvin. I also defined the conversion functions both way

abstract type Temperature end

struct Celsius <: Temperature

struct Kelvin <: Temperature

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

convert(::Type{Kelvin},  t::Celsius)     = Kelvin(t.value + 273.15)
convert(::Type{Celsius}, t::Kelvin)      = Celsius(t.value - 273.15)

However, when I run

promote(Kelvin(1), Celsius(1))

I get the following error, what did I do wrong? Shouldn’t promote promote the second argument which is of type Celsius to Kelvin?

ERROR: promotion of types Kelvin and Celsius failed to change any arguments
[1] error(::String, ::String, ::String) at .\error.jl:42
[2] sametype_error(::Tuple{Kelvin,Celsius}) at .\promotion.jl:308
[3] not_sametype(::Tuple{Kelvin,Celsius}, ::Tuple{Kelvin,Celsius}) at .\promotion.jl:302
[4] promote(::Kelvin, ::Celsius) at .\promotion.jl:285
[5] top-level scope at none:0

You need Base.X if you want to define X for your custom type. Or do import Base: X first. It works with me when I do that.


I see. So adding the below works

import Base: promote_rule, convert

Yes. You can do

Base.convert(blah... )


import Base: convert

Both are used. After reading and writing these things a few times, I still don’t know which is preferable. But, sometimes the details bear on the choice. In the case of convert, it’s not unreasonable that a package would have a convert function that is semantically unrelated to Base.convert. In this case, writing Base.convert might reduce cognitive load a bit when reading the code.

Also, I can never remember how to use parens and quoting to write something like Base.==(x, y) ... Out of frustration, I often do import Base: ==.