Understanding promotion


#1

I am exploring whether I can get some oo behaviour (data inheritance) in Julia, but that is not my question today. In that exploration I have this code that, to my surprise, fails:

abstract type Family end
type Parent <: Family
    a::Float64
end
type Child <: Family
    a::Float64
    parent::Parent
end
convert(::Type{Parent},x::Child) = x.parent
promote_rule(::Type{Child},::Type{Parent}) = Parent
combine(x::Parent,y::Parent) = print("Two Parents\n")
combine(x::T,y::T) where {T<:Family} = combine(x.parent,y.parent)

p = Parent(1.)
c = Child(3.,p)
combine(c,p)

The above fails. I expect Julia to use my promotion rule, convert Child to Parent, and thus call my second “combine” method (which then calls the first “combine” method.

No joy. Julia points out that there is no combine(::Child,::Parent) method. Where did I go wrong?


#2
  1. You need to either import convert and promote_rule or prefix with Base:
Base.convert(::Type{Parent},x::Child) = x.parent
Base.promote_rule(::Type{Child},::Type{Parent}) = Parent
  1. Julia doesn’t do implicit promotions, hence:
combine(x::Family, y::Family) = combine(promote(x, y)...)
julia> combine(c,p)
Two Parents

#3

Thanks. I keep forgetting to import!!! I’ll use the module.Function syntax in the future, as you suggest.

No implicit promotion, that’s very important. But I made that mistake for a reason: still playing around, I programmed “dual numbers” (automatic differentiation). There I have

convert(::Type{Dual}   ,x::Float64)               = Dual(x,0.)
promote_rule(::Type{Dual}   ,::Type{Float64)   = Dual
+(a::Dual,b::Dual)                        = Dual(a.x+b.x,  a.dx.+b.dx)
*(a::Dual,b::Dual)                        = Dual(a.x*b.x,  a.dx.*b.x.+b.dx.*a.x)

(no explicit call to “promote”) and I can combine Float64 and Dual…

It’s not the first time I get great help from you :grinning:


#4

That is because of: https://github.com/JuliaLang/julia/blob/b3241d7845f136d1f400f7283ae37128250d2883/base/promotion.jl#L276

+(x::Number, y::Number) = +(promote(x,y)...)
*(x::Number, y::Number) = *(promote(x,y)...)
-(x::Number, y::Number) = -(promote(x,y)...)
/(x::Number, y::Number) = /(promote(x,y)...)

i.e. all numbers promote with +, *, -, /.


#5

Thanks! I had writen an answer before you last post, but apparently did no “press return”:


Gosh am I slow!

Of course, somewhere in Julia there is something like

+(x,y) = +(promote(x,y)...)

which is why my “dual” code works while I need to promote in my “Family” code.

: )