It is well-known that class inheritance are "open" type-heirarchies and discriminated unions are "closed" type-heirarchies. However, while adding new subclasses is easy, adding new virtual functions requires modification of all the existing classes. Meanwhile, discriminated unions can add new functions easily.
| inheritance | discriminated union
new type | easy | hard
new function | hard | easy
OOP has been around enough that we've experienced the difficulty of "adding new methods to a type heirarchy," and in cases where "modify all the classes" is not a good option, we've come up with stuff like the Visitor pattern to add new functionality to existing types.
For example:
class Base Base.f() Base.g()
class A : Base A.f() A.g()
class B : Base B.f() B.g()
Because adding a virtual Base.h()
is difficult, we use visitor pattern, so future functionality can be encapsulated. This is basically the inheritance-based analogue of a pattern-matched function!
class H : BaseVisitor {
visit(A) { }
visit(B) { }
}
// this looks really similar to:
let H something =
match something with
| A -> ..
| B -> ..
TLDR: Is there a commonly seen abstraction (similar to the Visitor pattern) that deals with adding types to a discriminated union?