Multiple dispatch solves the expression problem so intuitively and straightforwardly that it's often hard to remember why it was a problem in the first place. In Julia, for example:
abstract Exp
immutable Lit <: Exp
value::Int
end
immutable Add <: Exp
left::Exp
right::Exp
end
evaluate(lit::Lit) = lit.value
evaluate(add::Add) = evaluate(add.left) + evaluate(add.right)
If you want to add a new operation, it's straightforward:
view(lit::Lit) = string(lit.value)
view(add::Add) = string("(", view(add.left), " + ", view(add.right), ")")
If you want to add a new type of expression, it's also straightforward:
immutable Mul <: Exp
left::Exp
right::Exp
end
evaluate(mul::Mul) = evaluate(mul.left) * evaluate(mul.right)
view(mul::Mul) = string("(", view(mul.left), " * ", view(mul.right), ")")
That's all there is to it. Of course, it's actually the
external dispatch that's crucial, not the multiple dispatch. (But multiple part is very handy for other things, especially in numerical work).