It seems like such an elegant idea, I’m somewhat surprised no one seems to have run with it earlier.
It's hard to get back to OOP once you're used to multiple dispatch. It's so natural and gets rid of the the question of "which class should I write this method in"?
Others mention CLOS, which I don't know too much about. MIT Scheme also supports multiple dispatch (https://www.gnu.org/software/mit-scheme/documentation/stable...). IIRC this is widely used in ScmUtils, the software library used in SICM (https://mitpress.mit.edu/books/structure-and-interpretation-...).
Common Lisp had multiple dispatch through the CLOS, but functions which needed to be fast were not multiple dispatch. For instance, you can't overload multiplication or additional in Common Lisp, you instead need to shadow them and replace them with generic functions, but doing so incurs a runtime performance overhead. This is (usually) not the case in Julia because it's JIT compiler was designed around multiple dispatch and it's multiple dispatch semantics were designed around it's JIT compiler (this is a big reason why static compilation in Julia has been such a hard problem to solve!).
Because Julia can have MD without additional overhead, it's use is absolutely ubiquitous throughout the ecosystem and Julia's own internals. This ubiquity is what confers most of the benefit because you can patch into the gigantic space of method combinations just about anywhere to override behaviour.
`fn(a, b, c)`
you really don't know which code it is running unless you know the types of `a`, `b`, and `c` which can't be exhaustively enumerated at the point of the writing the code or reading the code given that someone could use `fn` with totally different types.
This makes Julia readable, but as the code base grows, it can become unwieldly as well.
You really need `@which fn(a,b,c)` with known `a`, `b`, and `c` to see which function it is running.