Suppose an attack in a game depends on both the attacker and the defender. An archer attacking a knight may need different behavior from an archer attacking another archer. Multiple dispatch lets one function name, such as `attack!`, select a method using the types of both arguments. section 7.3.3
In Julia, a function is a name with methods attached to it; methods are not attached to the types of the objects they handle. A method signature specifies which argument types it accepts. For example, `attack!(a::Archer, b::Knight)` handles a different pair from `attack!(a::Archer, b::Archer)`. Use `methods(attack!)` at the REPL to inspect the available choices. section 7.3.3
julia> include("warriors.jl") #1
julia> methods(shoot!)
# 1 method for generic function "shoot!":
[1] shoot!(archer::Archer)
julia> methods(attack!)
# 2 methods for generic function "attack!":
[1] attack!(a::Archer, b::Archer)
[2] attack!(a::Archer, b::Knight)
julia> methods(mount!)
# 1 method for generic function "mount!":
[1] mount!(knight::Knight)
Compare the two `attack!` signatures: the function name stays the same, while the defender type changes. section 7.3.3
Add a missing matchup
Calling `attack!` with two knights initially produces a `MethodError`: no method matches `attack!(::Knight, ::Knight)`. Define `attack!(a::Knight, b::Knight)` to handle that pairing, then check `methods(attack!)` again. The function now has a third method, alongside the two archer matchups. section 7.3.3
This differs from single dispatch in object-oriented languages, where method selection uses one object’s type; handling different opponent types can then require `if`–`elseif` checks. It also differs from the function overloading described for Java and C++: when `battle!(a::Warrior, b::Warrior)` calls `attack!(a, b)`, the concrete combatant types can determine the matching attack method at runtime. section 7.4.1 section 7.4.2

