And, by the way, that specific example is doable (somewhat) in languages with static dispatch on multiple arguments, like C++'s function overloading.
We're just stuck with a statically determined result type; C++ won't statically model something like that an division of two Integer classes yields an Integer, otherwise a Ratio. If we don't need that, we are good to go:
// no OOP dispatch to see here
quaternion mul(const quaternion &left, double right);
quaternion mul(double left, const quaternion &right);
Static polymorphism may be good enough for a good many use cases, and runs fast too due to all dispatch being resolved at compile time.Multiple dispatch will not save you from the fact that if you have N different kinds of numbers, you may have to code all N*N combinations of some operations that don't commute, or N(N+1)/2 for those that do. It will give you a minimal-boilerplate way to write that though, with accurate modeling of situations where the result type depends on properties of run-time value.