Methods & Multiple Dispatch

A function is a name; methods are its implementations for different argument types.

15.1 Methods

A function is a name; methods are its implementations for different argument types. Julia picks the most specific method based on the types of all arguments (multiple dispatch).

area(s::Circle) = π * s.r^2
area(s::Point) = 0.0

describe(x) = "something"
describe(x::Number) = "a number"
describe(x::Integer) = "an integer"
describe(x::Integer, y::Integer) = "two integers"
describe(x::String, y) = "a string and something"

describe(1.5)      # "a number"
describe(2)        # "an integer"
describe(2, 3)     # "two integers"
methods(describe)  # list all methods
@which describe(2) # which method is called

15.2 Multiple dispatch in action

In single-dispatch OOP, a.meet(b) dispatches only on a. Julia dispatches on all arguments — this is how +(::Int, ::Float64), *(::Matrix, ::Vector) etc. are organized, and why packages compose so well.

abstract type Animal end
struct Dog <: Animal end
struct Cat <: Animal end

meet(a::Dog, b::Dog) = "sniffs"
meet(a::Dog, b::Cat) = "chases"
meet(a::Cat, b::Dog) = "hisses"
meet(a::Animal, b::Animal) = "ignores"

meet(Dog(), Cat())      # "chases"
meet(Cat(), Cat())      # "ignores"

15.3 Parametric methods

same_type(x::T, y::T) where {T} = true
same_type(x, y) = false
same_type(1, 2), same_type(1, 2.0)        # true, false

firstelem(v::AbstractVector{T}) where {T} = isempty(v) ? zero(T) : v[1]

15.4 Ambiguities

Check with Test.detect_ambiguities(MyModule).

f(x::Int, y) = 1
f(x, y::Int) = 2
# f(1, 2)  → MethodError: ambiguous. Fix by defining f(x::Int, y::Int)

15.5 Extending Base functions

Type piracy — extending a Base function for Base types (or another package's types) you don't own — is discouraged.

struct Vec2
    x::Float64
    y::Float64
end
Base.:+(a::Vec2, b::Vec2) = Vec2(a.x + b.x, a.y + b.y)
Base.:*(k::Real, v::Vec2) = Vec2(k * v.x, k * v.y)
Base.zero(::Type{Vec2}) = Vec2(0, 0)
Base.length(::Vec2) = 2
sum([Vec2(1, 2), Vec2(3, 4)])      # works because + and zero are defined

15.6 Callable objects (functors)

struct Polynomial
    coeffs::Vector{Float64}
end
(p::Polynomial)(x) = evalpoly(x, p.coeffs)
p = Polynomial([1, 2, 3]); p(2.0)     # 1 + 2x + 3x² = 17.0

15.7 Function barriers & dispatch on values

process(::Val{:fast}, x) = "fast path"
process(::Val{:safe}, x) = "safe path"
process(mode::Symbol, x) = process(Val(mode), x)