Julia Multiple Dispatch: Design Methods for All Argument Types
Multiple dispatch chooses methods using every argument's type. Check with @which and @code_warntype to keep Julia functions type-stable.
16 Jul 2025, 01:56 UTC

A shape calculator that slows down when types change
You are maintaining a Julia package that computes geometric areas. The first version handles circles:
struct Circle{T}
radius::T
end
area(c::Circle) = pi * c.radius^2
This works until a colleague passes a rectangle or mixes integer and floating-point radii. Julia uses multiple dispatch, choosing a method based on the types of every argument, not just the first. The compiler may select a generic fallback, recompile for each new type combination, or throw an ambiguity error. Treat method signatures as part of your performance contract, and verify dispatch before assuming a function is fast.
How Julia selects a method
When you call f(x, y), Julia examines the types of both x and y, matches them against the method table, and JIT-compiles specialized code for that combination. If no exact match exists, it walks up the type hierarchy. If two methods match equally well, the call is ambiguous and errors at runtime.
Worked example: areas for circles and rectangles
Define two shapes and an area method for each. Run these commands in the Julia REPL or a julia script.jl file. No special permissions are required.
struct Circle{T}
radius::T
end
struct Rect{T}
width::T
height::T
end
area(c::Circle{T}) where {T} = pi * c.radius^2
area(r::Rect{T}) where {T} = r.width * r.height
Version assumption: Julia 1.x.
Check which method runs
Use @which to see the selected method for a concrete type:
julia> @which area(Circle{Float64}(1.0))
Expected check: the output names area(c::Circle{T}) where T and shows the file and line where it is defined. If you instead see a generic fallback such as area(::Any), your method signature is too narrow or missing.
Inspect type stability
Use @code_warntype to see whether the compiler can infer a concrete return type:
julia> @code_warntype area(Circle{Float64}(1.0))
Expected check: the return type should be concrete, such as Float64. If you see Union{}, Any, or highlighted type unions, the path is type-unstable and the compiler cannot fully devirtualize the dispatch.
Trade-off: ambiguity and generality
Adding methods for every combination seems flexible, but it creates risk. If you define both area(c::Circle, r::Rect) and area(r::Rect, c::Circle), a call with mixed arguments may hit an ambiguity error. More methods also mean more compilation and longer precompile times.
Practical check: after adding a method, run @which with the exact argument types you expect in production. If the selected method surprises you, add an explicit type annotation or redesign the signature rather than relying on fallback behavior.
Actionable closing
- Write methods for the specific types your callers pass, not just the first argument.
- Verify dispatch with
@which(f, args)and type stability with@code_warntype f(args). - Benchmark critical paths with BenchmarkTools before and after adding type annotations.
- Keep the method surface small; ambiguity is easier to prevent than to debug at runtime.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.