Proposal: Add completeness checking to pattern matching draft specification
See original GitHub issueBackground
As noted in issue #180, many modern programming programs are data-focused, especially distributed applications which tend to store, manipulate, and move sets of data between different storage and computation points. One solution proposed to deal with this issue is a combination of records and pattern matching. Records provide a simple way to declare and structure the data types and pattern matching provides a way to destructure and manipulate the data.
Problem
Records provide a great way to represent the data and pattern matching provides a great way to manipulate the data, but there is currently no mechanism in the #180 proposal to ensure that the data and the logic remain in sync. The nature of records and pattern matching is that the data declaration code is often far from the data consumption code. In a distributed system it’s even more likely that a single data structure will be consumed and manipulated in various parts of the code base. If the data structure is ever modified, there is currently no mechanism in the draft to alert the programmer that all instances of manipulation logic must be updated.
Solution
Add completeness checking to certain switch
statements on certain record types. The core of this proposal is to provide a warning when a switch
statement does not handle every possible match on a type hierarchy. This proposal features two possible designs for this idea, presented in order of increasingly intrusive modification to the language.
Design 1
This design actually features no new syntax or semantics beyond that of proposal #180. The suggestion is to create a C# type heirarchy which can be guaranteed ‘complete’ with existing language features. In this case, complete means that it is not possible for a new subclass of the root member of the type hierarchy, so the compiler can be sure that any and all subclasses of the chosen switching type are visible in the current compilation.
We can construct this type hierarchy in existing C# with the following rules:
- All subclasses of the root type must be sealed, preventing any subclassing of any existing leaf types in the hierarchy.
- All constructors of the root type must be private, prevent any subclassing of the root type.
- As a consequence, all subclasses must be inner classes and thus must be in source in the current compilation
Here’s an example of the structure of this type hierarchy:
abstract class C
{
private C() {}
public sealed class C_1 : C {}
public sealed class C_2 : C {}
...
public sealed class C_n : C {}
}
This guarantees that switch
ing on an instance of type C
which explicitly matches C_1
…C_n
has matched against every possible instance of C
. The only thing which changes about the language specification is a requirement that the compiler produce a warning when not all cases are matched.
Design 2
There are a few disadvantages to Design 1:
- The mandated structure is complicated and brittle. Forgetting to mark any of the subclasses as
sealed
or adding any public constructors won’t produce a compiler error or warning, but the compiler will now silently skip the completeness check. - The structure is verbose – most of the
sealed
orprivate
markers are mostly part of the ‘incantation’ of completeness and are not directly related to the task at hand. - The relevant record instances are all nested classes, so all references require an extra layer of naming indirection.
Design 2 attempts to fix these problems by replacing much of the boiler plate with a new combination of modifiers on a type – abstract
+ sealed
. Under Design 2, marking the root type of a hierarchy as abstract sealed
will cause the structure from Design 1 to be generated by the compiler in lowering. The following example demonstrates what the structure from Design 1 looks like with an abstract sealed
type:
abstract sealed class C {}
public class C_1 : C {}
public class C_2 : C {}
...
public class C_n : C {}
In this case, most of the problems with Design 1 are solved, but new semantics are required to be added to the language.
Issue Analytics
- State:
- Created 9 years ago
- Reactions:4
- Comments:39 (19 by maintainers)
Top GitHub Comments
I like how it is done in Rust. I think we could inherit their enum ADT. In Haskell
I’d like to have In C#
And pattern matching like
Issue moved to dotnet/csharplang #486 via ZenHub