Depends on how the expression problem is defined. I prefer to define it as adding the new functionality without modifying existing code. I think this is a more general way of capturing the underlying dynamic. By this definition, e.g. Python still has the expression problem. Updating at least one of the dimensions requires updating an existing class. If Python kept methods separate from classes and had syntax for specifying overloads, it would have a solution for the expression problem. Of course, quite a bit else would have to change in that case... But IMO, the changes would be a positive improvement.
EDIT: besides, it is entirely possible to come up with static-ish variants of CLOS.
Does Python have the problem though? You can write:
def my_new_method(self):
...
setattr(MyClass, 'my_new_method', my_new_method)
In a separate file, and you can implement multidispatch as a library almost trivially. That's why the problem is not particularly interesting for dynamic languages.
Now, multidispatch in a statically typed language is a whole other ballgame and definitely has been proposed as a solution to the expression problem, but I personally disagree it is one, because every existing solution either abandons separate compilation (one of the original constraints) or "devolves" to dynamic typing.
I think the fair thing to say is that the expression problem just isn't an issue in dynamic languages. Even if the language doesn't have it built-in, reflection + monkey patching gives you a trivial library-based multidispatch solution.
That's not a great solution. Monkey patching has a history of downsides (that I do not think are inherent to multiple dispatch). Separate compilation is irrelevant in today's world when everyone just wants a self contained static binary anyway (and will put up with abominations like Docker to do it). And you're using a somewhat stylized definition of static vs dynamic typing there. After all, is subclassing really dynamic typing? (Though frankly, I think static typing isn't worth the semantic overhead.)
The Expression problem as formulated by Philip Wadler had both separate compilation and static typing as constraints. If you remove one or both you're solving a different problem.
Even if using ad-hoc monkey patching is messy, using it to implement multidispatch as a library is not (see: the python multimethods library which lets you do multidispatch with a decorator). Multidispatch is messy on its own from the simple fact that changing your set of imports can change the semantics of your code with no further changes on your part.
But my opinion is that multidispatch is not a solution to the expression problem (contrarily to wikipedia and many other places/people) because it necessarily fails the static typing constraint, and fails the separate compilation constraint to have decent performance.
Note that wikipedia also claims Object Algebras and Typeclasses as solutions (both of which do support static typing and separate compilation), but I also disagree that they're solving the problem, though for different reasons.
Personally I think the expression problem is a silly challenge, it's not really a thing to be solved. If you remove the constraints, then the solution is to use a dynamic language or a language with dynamic features. See C#'s dynamic for example, which gives you multimethods without trying to give you multimethods just from having to do overloading resolution at runtime.
Clos style multi methods do solve the expression problem.
CLOS is dynamically typed though, you don't really have the expression problem in dynamically typed languages, it's a static typing concern.
Depends on how the expression problem is defined. I prefer to define it as adding the new functionality without modifying existing code. I think this is a more general way of capturing the underlying dynamic. By this definition, e.g. Python still has the expression problem. Updating at least one of the dimensions requires updating an existing class. If Python kept methods separate from classes and had syntax for specifying overloads, it would have a solution for the expression problem. Of course, quite a bit else would have to change in that case... But IMO, the changes would be a positive improvement.
EDIT: besides, it is entirely possible to come up with static-ish variants of CLOS.
Does Python have the problem though? You can write:
def my_new_method(self):
...
setattr(MyClass, 'my_new_method', my_new_method)
In a separate file, and you can implement multidispatch as a library almost trivially. That's why the problem is not particularly interesting for dynamic languages.
Now, multidispatch in a statically typed language is a whole other ballgame and definitely has been proposed as a solution to the expression problem, but I personally disagree it is one, because every existing solution either abandons separate compilation (one of the original constraints) or "devolves" to dynamic typing.
I think the fair thing to say is that the expression problem just isn't an issue in dynamic languages. Even if the language doesn't have it built-in, reflection + monkey patching gives you a trivial library-based multidispatch solution.
That's not a great solution. Monkey patching has a history of downsides (that I do not think are inherent to multiple dispatch). Separate compilation is irrelevant in today's world when everyone just wants a self contained static binary anyway (and will put up with abominations like Docker to do it). And you're using a somewhat stylized definition of static vs dynamic typing there. After all, is subclassing really dynamic typing? (Though frankly, I think static typing isn't worth the semantic overhead.)
The Expression problem as formulated by Philip Wadler had both separate compilation and static typing as constraints. If you remove one or both you're solving a different problem.
Even if using ad-hoc monkey patching is messy, using it to implement multidispatch as a library is not (see: the python multimethods library which lets you do multidispatch with a decorator). Multidispatch is messy on its own from the simple fact that changing your set of imports can change the semantics of your code with no further changes on your part.
But my opinion is that multidispatch is not a solution to the expression problem (contrarily to wikipedia and many other places/people) because it necessarily fails the static typing constraint, and fails the separate compilation constraint to have decent performance.
Note that wikipedia also claims Object Algebras and Typeclasses as solutions (both of which do support static typing and separate compilation), but I also disagree that they're solving the problem, though for different reasons.
Personally I think the expression problem is a silly challenge, it's not really a thing to be solved. If you remove the constraints, then the solution is to use a dynamic language or a language with dynamic features. See C#'s dynamic for example, which gives you multimethods without trying to give you multimethods just from having to do overloading resolution at runtime.