Claim (false): Julia isn't multiple dispatch but overloading

Fair enough, here is an example, I guess I’m eating my own words a bit here, but to be fair most of the time you call Julia in examples people think are “multiple dispatch” are just overloading, the example below qualifies as multiple dispatch. Such cases exist but are a tiny minority.

abstract type Animal end
struct Lizard <: Animal name :: String end
struct Rabbit <: Animal name :: String end

race(l::Lizard, r::Rabbit) = "$(l.name) wins in wall climbing."
race(r::Rabbit, l::Lizard) = "$(r.name) wins in a normal race."
race(a::T, b::T) where T <: Animal = "$(a.name) and $(b.name) run forever."

function meet(a::Animal, b::Animal) 
    println("$(a.name) meets $(b.name) and they race!")
    println("Outcome: $(race(a,b))")
end

function spawnAnimal()
  rn = rand()
  if(rn > 0.5)
    return Lizard("Bayi")
  end
  return Rabbit("Sally")
end

function randomAnimals()
  a::Animal = spawnAnimal()
  b::Animal = spawnAnimal()
  meet(a, b) 
end


import InteractiveUtils: @code_warntype;

@code_warntype randomAnimals()

Output:

Variables
  #self#::Core.Compiler.Const(randomAnimals, false)
  a::Union{Lizard, Rabbit}
  b::Union{Lizard, Rabbit}

Body::Nothing
1 ─ %1 = Main.spawnAnimal()::Union{Lizard, Rabbit}
│   %2 = Base.convert(Main.Animal, %1)::Union{Lizard, Rabbit}
│        (a = Core.typeassert(%2, Main.Animal))
│   %4 = Main.spawnAnimal()::Union{Lizard, Rabbit}
│   %5 = Base.convert(Main.Animal, %4)::Union{Lizard, Rabbit}
│        (b = Core.typeassert(%5, Main.Animal))
│   %7 = Main.meet(a, b)::Core.Compiler.Const(nothing, false)
└──      return %7

This is true dynamic behaviour because the compiler doesn’t know which method it has to dispatch until the function is called.

4 Likes