3 ms·
C#'s pattern matching does not include the ability to check for exhaustiveness, which is need to make pattern matching comparable in utility and power to inheri
by platz 7y ago
C#'s pattern matching does not include the ability to check for exhaustiveness, which is need to make pattern matching comparable in utility and power to inheritance (i.e. so you can actually replace inheritance w/ pattern matching.)
Much like how the compiler assists you when writing subclasseses so that you've implemented all the required methods, real Pattern Matching (F#) gives you the same guarantees from the compiler that you've handled all the possible cases appropriately.
Exhaustiveness checking is what elevates pattern matching to the other side of the expression problem, which is about getting feedback from the compiler to help you write programs.
a glorified switch statement does not help you do that, and so you still have to write subclasses to get compiler feedback.
- mjburgess 7y agoDo you know why they dont do exhaustiveness checking? They don't in scala either and I had assumed that was to do with type erasure. Given F# does however, presumably there is no limitation to C# doing it?
- platz 7y ago> Do you know why they dont do exhaustiveness checking? Because then you would need to limit matching to some kind of closed algebraic data type (ADT) so that compiler knows what is left to be matched on, or provide a catch-all clause. I thought scala does produce partial warnings matching on case classes
- seemslegit 7y agoNor should it, assuming a fixed set of derived classes is a bad OOP/SOLID practice as it breaks the "objects should be opened to extension" principle and prior to introduction of RTTI in C++ it wasn't even a design possibility - you either specified the concrete type or relied on abstraction and polymorphism.
- platz 7y agoThat's not what 'fixed set' means here. It's 'fixed' in the same sense as the compiler knowing what are the subclasses of a base class. Just as you can always 'extend' the OOP base class by adding a subclass, you can always 'extend' an algebraic data for by adding another case. Both can be extended, but more importantly they can be enumerated.
- seemslegit 7y agoThat's correct, and exhaustively enumerating derived classes at compile-time is usually a bad design either via RTTI type-switching or pattern matching (which is really just syntactic sugar for the former). It prevents for example having derived types injected at run-time in a plugin setting. Explicitly matching a subset of well-known derived types otoh is a practical compromise in many cases.
- platz 7y ago> enumerating derived classes at compile-time is usually a bad design Depends on what kinds of guarantees you want. At the extreme, you can make everything in the system late-bound, and then you simply have a dynamic language instead of a static one.
- seemslegit 7y ago> enumerating derived classes at compile-time is usually a bad design did you deliberately omit the word 'exhaustively' here ? late-bound != dynamically typed, that's exactly the point - you wish to preserve to the ability to have types loaded at run time without knowing their exact type at compile time while still enjoying strong type guarantees. We can do without run-time loading scenarios at all and still see why this is bad, if one adds a derived class to the code base and then has to address it in a bunch of places rather than have all the new functionality localized to the class then it's a smell
- jcelerier 7y ago> It's 'fixed' in the same sense as the compiler knowing what are the subclasses of a base class. ... but the compiler does not know ? new classes are loaded at run-time all the time with e.g. plug-in systems, etc.
- dmitriid 7y agoErlang doesn't check for exhaustiveness either, and pattern matching is awesome :) Without exhaustiveness it might not be feasible for a statically types PL though, but I don't know nearly enough to even speculate on that.
- platz 7y agoI'm not implying exhaustiveness is always necessary, I'm saying if you want feedback from the compiler to support a type-driven extend-refactor workflow that is commensurate with inheritance-based typechecking, as is one of the major arguments for inheritance and subclassing to work they way it does in a static language, then you need exhaustivess to deliver the guarantees and maintainability experience if pattern matching is to replace inheritance+sublassing. Dynlangs here do not apply.