we're in 2020
using value = std::variant<int, float, string>;
void foo(value a, value b, value c) {
struct {
void operator(int, float, string) { }
void operator(float, int, string) { }
void operator(float, string, int) { }
///... etc...
} visitor;
std::visit(visitor, a, b, c);
}
works fine (and has worked fine since a long time with either boost.variant or in C++11 with eggs.variant, etc...)More importantly, this is not as powerful as true multiple dispatch like in CLOS. There is no way to call other methods, there is no way to specialize partially (e.g. combine the variant with subtypes, and define methods only for some combinations of subtypes and super types on the parameters, and allow the implementation to choose the most specific one).
Edit: to add a few more details on my second point. CLOS (and I assume Julia too) allows you to call 'parent' implementations from a child implementation, similar to calling base type methods in virtual dispatch - you can call the next-most-specific method that would have matched the params, which you can't with the visitor. Also, say I have a generic function F(Foo, Bar). I could define methods for F(FooSubtype1, Bar) and F(Foo, BarSubtype1), and then call F(FooSubtype1() , BarSubtypeOfSubtype1() ) and have the right implementation get called (F(Foo, BarSubtype1)).
It's far from the same thing though, less convenient and less flexible.
Multiple dispatch doesn't require a centralized declaration of the possible types and allows putting implementations in different files.
Java, Python and C++ require Visitor machinery that you ahev to write, for each case, to implement multiple dispatch (and Visitor only works for double dispatch, if you want more than 2 arguments you need even more boilerplate). Julia and CLOS essentially have that machinery built in.
In case this is not clear, multiple dispatch refers to choosing which method implementation to call based on the specific runtime subtypes of all function arguments, not just the runtime type of `this` (which is all that Java, C++, and even Python can dispatch based on). It's basically like applying C++ function overloading rules at runtime.
> The article's concern is real but overstated in my opinion. When adding a new function in a hierarchy you'll either have a good default or you'll be implementing via type matching branches or 10 implementations in each type behind multiple dispatch.
That's assuming you can modify all existing implementations. If you can't (e.g. if you wanted to modify Iterable to add a new method) then the solution is just.. don't do that. Note that the article also doesn't offer a solution - they're just showing that multiple dispatch can be used to implement a different construct with different limitations (GADTs).