question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Add completeness checking to pattern matching draft specification

See original GitHub issue

Background

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:

  1. All subclasses of the root type must be sealed, preventing any subclassing of any existing leaf types in the hierarchy.
  2. 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 switching on an instance of type C which explicitly matches C_1C_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:

  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.
  2. The structure is verbose – most of the sealed or private markers are mostly part of the ‘incantation’ of completeness and are not directly related to the task at hand.
  3. 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:closed
  • Created 9 years ago
  • Reactions:4
  • Comments:39 (19 by maintainers)

github_iconTop GitHub Comments

3reactions
KalitaAlexeycommented, Apr 3, 2016

I like how it is done in Rust. I think we could inherit their enum ADT. In Haskell

data Expression = Number Int
                | Add Expression Expression
                | Minus Expression
                | Mult Expression Expression
                | Divide Expression Expression

I’d like to have In C#

enum Expression {
    Number(int Number),
    Add(Expression LeftOperand, Expression RightOperand),
    Minus(Expression Expression),
    Mult(Expression LeftOperand, Expression RightOperand),
    Divide(Expression LeftOperand, Expression RightOperand),
}

And pattern matching like

void ProcessExpression(Expression expression)
{
    switch (expression)
    {
    case Expression::Number n:
        ProcessNumber(n);
        break;
    case Expression::Add a:
        ProcessAdd(a);
        break;
    case Expression::Minus m:
        ProcessMinus(m);
        break;
    case Expression::Mult m:
        ProcessMult(m);
        break;
    }
    case Expression::Divide d:
        ProcessDivide(d);
        break;
}
0reactions
gaftercommented, Apr 21, 2017

Issue moved to dotnet/csharplang #486 via ZenHub

Read more comments on GitHub >

github_iconTop Results From Across the Web

Proposal: Add completeness checking to pattern matching ...
Add completeness checking to certain switch statements on certain record types. The core of this proposal is to provide a warning when a...
Read more >
Pattern matching using is and as
This paper agrees with the existing pattern matching proposals' ... new dynamic types can just add both operator is and operator as, ...
Read more >
is case : pattern-match boolean expressions
Future Direction: Negation. This proposal provides a convenient way to check if an expression matches a pattern, but while that composably ...
Read more >
JEP 406: Pattern Matching for switch (Preview)
There are four major design issues when case labels can have patterns: Enhanced type checking; Completeness of switch expressions and statements ...
Read more >
Type Parameters Proposal
This is the design for adding generic programming using type parameters to the Go language. This design has been proposed and accepted as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found