I’ve been watching an interesting video in which type classes in Haskell are used to solve the so-called “expression problem”. About 15 minutes in, it shows how type classes can be used to “open up” a datatype based on a discriminated union for extension — additional discriminators can be added separately without modifying / rebuilding the original definition.
I know type classes aren’t available in F#, but is there a way using other language features to achieve this kind of extensibility? If not, how close can we come to solving the expression problem in F#?
Clarification: I’m assuming the problem is defined as described in the previous video
in the series — extensibility of the datatype and operations on the datatype with the features of code-level modularization and separate compilation (extensions can be deployed as separate modules without needing to modify or recompile the original code) as well as static type safety.
As Jörg pointed out in a comment, it depends on what you mean by solve. If you mean solve including some form of type-checking that the you’re not missing an implementation of some function for some case, then F# doesn’t give you any elegant way (and I’m not sure if the Haskell solution is elegant). You may be able to encode it using the SML solution mentioned by kvb or maybe using one of the OO based solutions.
In reality, if I was developing a real-world system that needs to solve the problem, I would choose a solution that doesn’t give you full checking, but is much easier to use.
A sketch would be to use
objas the representation of a type and use reflection to locate functions that provide implementation for individual cases. I would probably mark all parts using some attribute to make checking easier. A module adding application to an expression might look like this:This does not give you any type-checking, but it gives you a fairly elegant solution that is easy to use and not that difficult to implement (using reflection). Checking that you’re not missing a case is not done at compile-time, but you can easily write unit tests for that.