Draft spec for records and pattern-matching in C#

Topics: C# Language Design
Developer
Aug 6, 2014 at 10:44 PM
The following link is to a draft specification for record types and pattern matching in C# for which a prototype will be published in a few weeks.

Pattern Matching for C#

This is an experimental language feature that has not yet been vetted by the C# language design group. The prototype is intended to help prepare for such a discussion.
Aug 7, 2014 at 12:56 AM
Edited Aug 7, 2014 at 1:12 AM
Fantastic! It's pattern matching and it looks C#!

Some early question:

1.- what is the syntactic difference between a Pattern class and a class with primary constructor? The semicolon?

2.- Can is expression be combined?
if((obj is Dog d) || (obj is Cat c))
{
    //Are d and c definitely assigned? How this works currently with declaration expression?
}
3.- I assume that ?: expression in combination with is expression is the way to create expression-style patter matching. Some examples?
Aug 7, 2014 at 1:16 AM
My first suggestion is to evaluate the Type Pattern separately from this proposal. It could instead be added as an extension of the current is operator.

I notice there is an ambiguity between the current switch statement and the proposed pattern matching functionality in the case where all of the case statements use a constant-expression. You should explicitly state that the order of case statements is only considered when at least one case statement uses a complex-pattern which is not a constant-pattern.

You also gain an advantage in declaring syntax if you include type as an option within complex-pattern instead of declaring it separately, which could be especially relevant if you use the current scoping rules for identifiers within a switch statement. For example, unless the scoping rules are revised, the example Deriv method shown in §1.8.4 (Arithmetic simplification) would result in multiple declarations of the variables Left and Right within the same scope.
Developer
Aug 7, 2014 at 2:11 AM
Olmo wrote:
Fantastic! It's pattern matching and it looks C#!

Some early question:

1.- what is the syntactic difference between a Pattern class and a class with primary constructor? The semicolon?
The "record" modifier.
2.- Can is expression be combined?
if((obj is Dog d) || (obj is Cat c))
{
    //Are d and c definitely assigned? How this works currently with declaration expression?
}
Neither d nor c would be definitely assigned in this block. The rule is that a variable declared in a pattern is definitely assigned "when true" - when the enclosing "is" pattern match operator is true. In this case neither is definitely true to get into the if's controlled block.
3.- I assume that ?: expression in combination with is expression is the way to create expression-style patter matching. Some examples?
You could do that. The spec doesn't directly support expression-style pattern matching except the "is" operator.
Aug 7, 2014 at 2:13 AM
More thoughts:

1.- Just being able to do is to compare two instances with equals without null problems is already cool.
if(p1 is p2){...}
2.- Idea for syntax to match arbitrary objects:
if(p1 is { Name = "John"}) // property
if(p1 is { Name = Length(10)}) // arbitrary pattern
if(list is { "john"}) // enumerating values
if(list is { [0] = "john"}) // indexer
if(dic is { ["Name"] = "john"}) // indexer
3.- will this pattern be converted as expressions for LINQ providers?
Developer
Aug 7, 2014 at 2:18 AM
sharwell wrote:
My first suggestion is to evaluate the Type Pattern separately from this proposal. It could instead be added as an extension of the current is operator.
The prototype supports that as a special case, yes.
I notice there is an ambiguity between the current switch statement and the proposed pattern matching functionality in the case where all of the case statements use a constant-expression. You should explicitly state that the order of case statements is only considered when at least one case statement uses a complex-pattern which is not a constant-pattern.
There is no ambiguity. The existing switch statement is a special case of the generalized construct.
You also gain an advantage in declaring syntax if you include type as an option within complex-pattern instead of declaring it separately, which could be especially relevant if you use the current scoping rules for identifiers within a switch statement. For example, unless the scoping rules are revised, the example Deriv method shown in §1.8.4 (Arithmetic simplification) would result in multiple declarations of the variables Left and Right within the same scope.
I added "The scope of a variable declared in a pattern of a switch-label is the containing switch-section. "
Developer
Aug 7, 2014 at 2:24 AM
Olmo wrote:
More thoughts:

1.- Just being able to do is to compare two instances with equals without null problems is already cool.
if(p1 is p2){...}
It doesn't do that.
2.- Idea for syntax to match arbitrary objects:
if(p1 is { Name = "John"}) // property
if(p1 is { Name = Length(10)}) // arbitrary pattern
if(list is { "john"}) // enumerating values
if(list is { [0] = "john"}) // indexer
if(dic is { ["Name"] = "john"}) // indexer
It doesn't do that either. I'm not sure what you imagine those would do or how you imagine them working under the hood.
3.- will this pattern be converted as expressions for LINQ providers?
I don't know what you have in mind. They already are (boolean) expressions.
Aug 7, 2014 at 2:29 AM
nmgafter wrote:
There is no ambiguity. The existing switch statement is a special case of the generalized construct.
This is correct, specifically because the labels in the existing switch statement are unique compile-time constants (and therefore order does not matter).
Aug 7, 2014 at 2:31 AM
Rather than define a type that supports pattern-based matching by the presence of value-parameters, why not define it according to the presence of the operator is method? The value-parameters construct could then be syntactic sugar for providing such a method in a standard way for the common case.
Developer
Aug 7, 2014 at 3:08 AM
Edited Aug 7, 2014 at 3:11 AM
sharwell wrote:
Rather than define a type that supports pattern-based matching by the presence of value-parameters, why not define it according to the presence of the operator is method? The value-parameters construct could then be syntactic sugar for providing such a method in a standard way for the common case.
That is exactly how the specification is written, though since class value parameters already mean something due to the primary constructor feature, you need the "record" modifier too to get a compiler-provided "operator is". In a record class the value parameters also define properties (one for each parameter), GetHashCode, Equals, and possibly ToString.
Aug 7, 2014 at 8:13 AM
Edited Aug 7, 2014 at 8:15 AM
I think that '*' looks a little out of place in pattern mattching context. Wouldn't some short keyword like any look/feel better?

Also, I hope it would be also applicable to other contexts like writing obj.myEvent += (o, *) => ....
Aug 7, 2014 at 8:44 AM
Edited Aug 7, 2014 at 9:25 AM
nmgafter wrote:
Olmo wrote:
More thoughts:

1.- Just being able to do is to compare two instances with equals without null problems is already cool.
if(p1 is p2){...}
It doesn't do that.
If I understood it properly:
relational-expression: 
   relational-expression is complex-pattern  <-- expand this
   relational-expression is type

complex-pattern: 
   constant-pattern <-- expand this
   type identifier 
   recursive-pattern 
   recursive-pattern identifier

constant-pattern: 
   constant-expression <-- expand this
Ough I get it, is not a constant-expression. Is there a reason for that? If it's going to evaluate Equals anyway why not let arbitrary expressions too. Having first-level support to call object.Equals(e1, e2) will be nice.
2.- Idea for syntax to match arbitrary objects:
if(p1 is { Name = "John"}) // property
if(p1 is { Name = Length(10)}) // arbitrary pattern
if(list is { "john"}) // enumerating values
if(list is { [0] = "john"}) // indexer
if(dic is { ["Name"] = "john"}) // indexer
It doesn't do that either. I'm not sure what you imagine those would do or how you imagine them working under the hood.
They could deconstruct the object with a similar syntax to object initializers.
  • Using properties could just get the value in this property and continue matching from there.
  • Plain expression without properties could just take the IEnumerator and match each consecutive value
  • Indexers are tricky because they throw exceptions. If checking for Length / Count / ContainsKey is too add-hoc then well need another solution.
3.- will this pattern be converted as expressions for LINQ providers?
I don't know what you have in mind. They already are (boolean) expressions.
I mean if there will be a PatternExpression hierarchy in System.Expressions so things like this can be made:
var longCatgs =  from a in Table<Animal>()
                         where a is Cat(length: 10)
                         select a; 
And translated to SQL. It's early anyway, just to know if System.Expressions support will be considered.
Aug 7, 2014 at 9:42 AM
I agree with Olmo: it's pattern matching and it looks like C#. Yay!

Pattern Classes

The difference between pattern classes and "normal" classes is still not completely clear to me. Not in terms of what the are, but in terms of their purpose and usage.
  1. Is it correct that I use a "normal" class (i.e. not a pattern class) in pattern matching? For example I could do this with my existing Customer entity class, as long as it overloads "is", right?
if(c is Customer(var Surname))
{
  .....
  1. And so a "record" class just gives me the automatic generation of various members, right?
  2. Which of those auto-generated members is required by the pattern matching? Are they all required, or does pattern matching only require the "is" overload?

Usage

  1. The "if (c is Polar(var R...." example doesn't parse well in my brain. I think the reason is that it's so ingrained in me that "is" does not do user-defined conversions. And yet saying "if my-cartesian-coordinate is Polar" implies (and in fact performs) a conversions of sorts. Are there any other syntactic options that could be considered?

Switch statement

  1. I really like the idea of finding a way to declare that the switch statement is meant to be complete. I wonder if this is also an opportunity to tidy/improve a few more aspects of switch. E.g. introduce the concept of a switch expression (looks like statement, but the word "return" can be ommitted from each case, and result of swictch can be assigned to a variable; or even define the new pattern-matched switch to be totally different from the "old" switch and therefore not need "break" statements to terminate each case.
Aug 7, 2014 at 2:28 PM
nmgafter wrote:
if((obj is Dog d) || (obj is Cat c))
{
    //Are d and c definitely assigned? How this works currently with declaration expression?
}
Neither d nor c would be definitely assigned in this block. The rule is that a variable declared in a pattern is definitely assigned "when true" - when the enclosing "is" pattern match operator is true. In this case neither is definitely true to get into the if's controlled block.
Couldn't the rule be changed so that both c and d are always visible from inside both the true_block and the false_block of the if? It could be useful.
If obj is a Dog, we would have d == obj as Dog, and c == null because the second is was not reached (even if somehow Cat inherits Dog or vice versa).
If obj is not a Dog, we would have d == obj as Dog, and c == obj as Cat.
Aug 7, 2014 at 4:10 PM
Awesome! Can't wait to get my hands on the prototype!

The examples in the document are wrong, aren't they? Instead of public class Cartesian(double X, double Y); the example should read public record class Cartesian(double X, double Y); - note the record keyword before class. Actually, I'd even vote for dropping the class keyword here, as it seems completely redundant (unless, of course, you also implement record structs, which I would be in favor of; this should be useful occasionally in garbage collector constrained code).

So far this all sounds pretty good and is a very welcome addition to C# - I've used a lot of F# recently precisely because C# is missing a concise way to define records, discriminated unions and pattern matching. C#'s syntactic overhead is still greater than that of F#, but that is to be expected and is more than offset by the better tooling Visual Studio provides.

I'd definitely like to see pattern matching on arrays and lists and ideally dictionaries as well.
Developer
Aug 7, 2014 at 6:38 PM
johnrusk wrote:
I agree with Olmo: it's pattern matching and it looks like C#. Yay!

Pattern Classes

The difference between pattern classes and "normal" classes is still not completely clear to me. Not in terms of what the are, but in terms of their purpose and usage.
  1. Is it correct that I use a "normal" class (i.e. not a pattern class) in pattern matching? For example I could do this with my existing Customer entity class, as long as it overloads "is", right?
if(c is Customer(var Surname))
{
  .....
  1. And so a "record" class just gives me the automatic generation of various members, right?
  2. Which of those auto-generated members is required by the pattern matching? Are they all required, or does pattern matching only require the "is" overload?
All of that is right. It just requires an overloaded operator is.

Usage

  1. The "if (c is Polar(var R...." example doesn't parse well in my brain. I think the reason is that it's so ingrained in me that "is" does not do user-defined conversions. And yet saying "if my-cartesian-coordinate is Polar" implies (and in fact performs) a conversions of sorts. Are there any other syntactic options that could be considered?
This does not perform a conversion. It invokes the "is" operator of Polar.
Developer
Aug 7, 2014 at 6:40 PM
eldritchconundrum wrote:
nmgafter wrote:
if((obj is Dog d) || (obj is Cat c))
{
    //Are d and c definitely assigned? How this works currently with declaration expression?
}
Neither d nor c would be definitely assigned in this block. The rule is that a variable declared in a pattern is definitely assigned "when true" - when the enclosing "is" pattern match operator is true. In this case neither is definitely true to get into the if's controlled block.
Couldn't the rule be changed so that both c and d are always visible from inside both the true_block and the false_block of the if? It could be useful.
If obj is a Dog, we would have d == obj as Dog, and c == null because the second is was not reached (even if somehow Cat inherits Dog or vice versa).
If obj is not a Dog, we would have d == obj as Dog, and c == obj as Cat.
The pattern matching language feature is intended to be a safer alternative to the buggy practice of using null values.
Developer
Aug 7, 2014 at 6:42 PM
Expandable wrote:
Awesome! Can't wait to get my hands on the prototype!

The examples in the document are wrong, aren't they? Instead of public class Cartesian(double X, double Y); the example should read public record class Cartesian(double X, double Y); - note the record keyword before class. Actually, I'd even vote for dropping the class keyword here, as it seems completely redundant (unless, of course, you also implement record structs, which I would be in favor of; this should be useful occasionally in garbage collector constrained code).
Yes, there should be "record" in the declaration in 1.1.1.5.

Yes, we'd like to do this for structs too. It is just a bit easier to start with just classes.
I'd definitely like to see pattern matching on arrays and lists and ideally dictionaries as well.
Agreed. And value tuples too.
Aug 7, 2014 at 7:00 PM
I second the idea of attempting to come up with another term other than is, but I can't think of any, and is seems like the best option to me.

I think this is awesome, and I find it really neat that is is not an overloadable operator. If I understand it correctly that means we could override the existing implementation of is? (ie provide an operator that doesn't do the deconstruction, just does the check).

If this is so, perhaps as should be overloadable now as well? That will probably be very tricky though because the whole point of as is that the type on the left is unknown at compile time, and yet operators need to know the type at compile time. It may not be possible to solve this issue, but thought I'd suggest it in case someone else had a better solution.
Aug 7, 2014 at 7:18 PM
Edited Aug 7, 2014 at 7:19 PM
I would really like to be able to do something like
var name = "Steve";

return switch(name)
{
    case "Steve" : return "Ballmer";
    case "Bill" : return "Gates";
};
Can you make this happen? Otherwise it's not functional but old-school imperative.
Aug 7, 2014 at 8:25 PM
Edited Aug 7, 2014 at 10:28 PM
The proposed C# record type's implementation and behaviour appears very close to F#'s, i.e. the generated constructors, properties, and the Equals and GetHashCode methods appear equivalent. Currently the main divergence in the specification appears to be that the proposed C# record types are not sealed and it is not specified whether the record class would implement IEquatable<T> and IStructrualEquatable.

I think it would be a nice story for interop if the implementations of F# and C# record types could converge and be made to be compatible.
Aug 7, 2014 at 8:40 PM
Edited Aug 7, 2014 at 8:41 PM
dsaf wrote:
var name = "Steve";

return switch(name)
{
    case "Steve" : return "Ballmer";
    case "Bill" : return "Gates";
};
um... why not:
var name = "Steve";

switch(name)
{
    case "Steve" : return "Ballmer";
    case "Bill" : return "Gates";
    default: throw new InvalidNameException();
};
This does work with current C#
Aug 7, 2014 at 9:17 PM
Edited Aug 7, 2014 at 9:18 PM
ptrelford wrote:
I think it would be a nice story for interop if the implementations of F# and C# record types could converge and be made to be compatible.
Interesting point. Could F# record types and discriminated unions be treated as record classes? And vice versa? Including the possibility to pattern match?
Aug 7, 2014 at 9:30 PM
How about special syntax for nullable types?

Instead of
T? val = ...;
if (val is T x) { ... }
do
if (val is ?x) { ... }
or perhaps
if (val is var x) { ... }
Aug 7, 2014 at 9:31 PM
Edited Aug 7, 2014 at 9:33 PM
dsaf wrote:
I would really like to be able to do something like
var name = "Steve";

return switch(name)
{
    case "Steve" : return "Ballmer";
    case "Bill" : return "Gates";
};
Can you make this happen? Otherwise it's not functional but old-school imperative.
I'm with you that switch is too imperative, but I don't know a better syntax. Your still looks imperative because the return and semicolons, maybe something like:
return switch(name)
{
     case "Steve" ? "Ballmer":
     case "Bill" ? "Gates":
};
Anyway just using combining ?: is not that bad:
return name == "Steve" ? "Ballmer":
       name == "Bill" ? "Gates":
       new InvalidOperationException().Throw<string>() 
I've created an extension method Throw<T> over exception to finish this ?: chains.
Aug 7, 2014 at 9:52 PM
I understand that pattern-like patterns (anything involving "is complex-pattern") require record classes. Will there be a way to enlighten other types too? Anonymous types seem like they should be able to adopt all these features magically (although probably not as magically as to not require recompilation).

Putting record classes aside, I imagine this would be useful matching types for e.g. serialization. But let's say you're supposed to match IList<T> - that means you'll start with an object (or something similarly amorphous), and there will have to be some sort of syntax for introducing a new generic type parameter to the switch statement or case. How would something like that be written, or would it simply not be, at the level of this draft spec and prototype?
Aug 7, 2014 at 10:14 PM
nmgafter wrote:
All of that is right. It just requires an overloaded operator is.
So what if we didn't have record classes then? What if we could just get a compiler-generated "is" by asking for it, on any class, with some sort of compact syntax. E.g.
Public class Foo(int Bar)
is;
  1. The "if (c is Polar(var R...." example doesn't parse well in my brain. I think the reason is that it's so ingrained in me that "is" does not do user-defined conversions. And yet saying "if my-cartesian-coordinate is Polar" implies (and in fact performs) a conversions of sorts. Are there any other syntactic options that could be considered?
This does not perform a conversion. It invokes the "is" operator of Polar.
I realise its not actually doing a conversion, but the invocation of "is" on the un-related class seems at odds with the spirit of the existing is operator. I would suffer no such confusion if the new operator was called, for example, "match".
Aug 7, 2014 at 10:16 PM
Edited Aug 7, 2014 at 10:19 PM
So what if we didn't have record classes then? What if we could just get a compiler-generated "is" by asking for it, on any class, with some sort of compact syntax. E.g.
Public class Foo(int Bar)
is;
I'd prefer to see an attribute for something like this. Attributes invoke all sorts of magic so people would be used to it.

EDIT: Also the same kind of thing should exist for Equals, GetHashCode etc. Would be nice to have a set of attributes you could decorate a class with to get all of the boiler plate functions.
Developer
Aug 7, 2014 at 10:18 PM
JesperTreetop wrote:
I understand that pattern-like patterns (anything involving "is complex-pattern") require record classes. Will there be a way to enlighten other types too? Anonymous types seem like they should be able to adopt all these features magically (although probably not as magically as to not require recompilation).
We would probably want to do this for anonymous types. Since anonymous types do not cross assembly boundaries, you would not be able to observe the extended capabilities unless you wrote - and compiled - code into the same assembly.

But wouldn't it be nice if something like anonymous types could cross assembly boundaries?
Putting record classes aside, I imagine this would be useful matching types for e.g. serialization. But let's say you're supposed to match IList<T> - that means you'll start with an object (or something similarly amorphous), and there will have to be some sort of syntax for introducing a new generic type parameter to the switch statement or case. How would something like that be written, or would it simply not be, at the level of this draft spec and prototype?
We looked at it, and the CLR can't efficiently express this. Instead you'd have to match using the base IList (nongeneric) type.
Aug 7, 2014 at 10:21 PM
Suchiman wrote:
um... why not:
var name = "Steve";

switch(name)
{
    case "Steve" : return "Ballmer";
    case "Bill" : return "Gates";
    default: throw new InvalidNameException();
};
This does work with current C#
This has three exit points.

If the idea of exercise is making things that work with current C#, then we can just implement the whole thing with monads and release a library like Linq. But the idea is to improve the language instead (I hope).
Aug 7, 2014 at 10:25 PM
Olmo wrote:
return switch(name)
{
     case "Steve" ? "Ballmer":
     case "Bill" ? "Gates":
};
I like this, except colons should be semicolons.
Anyway just using combining ?: is not that bad:
It is bad, IMHO. If we want to be conservative than almost anything could be done with another set of monadic libraries.
Aug 7, 2014 at 11:16 PM
In switch, what about re-using the same pattern matching operator as used elsewhere (i.e. "is" in current proposal). Would look like this
 switch(name)
{
    is "Steve": return "Ballmer";
    is "Bill": return "Gates";
}
So there would be two types of switch : the old style one with case, where each case must be string or numeric; and new style with "is", which uses pattern matching. New style could break with tradition and allow omitting returns, and no longer requiring "breaks".

New style doesn't need "default" since "is *" should do the same job, right?
Aug 7, 2014 at 11:46 PM
Ahh, i was just confused by your supposed Syntax that was IMHO even more boilerplate for having to return Switch with another return so i didn't saw any enhancement in this form. Olmos proposal looks better in that case, but i'm with you and ':'
Aug 7, 2014 at 11:56 PM
Edited Aug 8, 2014 at 12:02 AM
Thanks nmgafter for all the answers.

Some more questions however :)

1.- Whats the benefit of making record classes invocable? (1.2)

2.- There will be a way to know if a class is a record class, something like typeof(Cartesian).IsRecord, or is just a pattern (like static classes for example).

3.- Maybe is operators should only be allowed in static classes (maybe excluding the type itself) like extension methods do. I assume extensions methods do that to reduce the search space. It's also odd that 'is' operator will be the only 'extensional' operator.

4.- What´s the reason for constant-patterns to be constant, and not just expression patterns? I would like to write if(p1 is p2) instead of if(p1.Equals(p2)) (p1 should be not null) or if(object.Equals(p1,p2)) (little bit too long...)

5.- Why not let recursive-pattern also have an identifier:

recursive-pattern:
type ( subpattern-list opt. ) identifier opt.
if(box is Rectangle(Point(0,0), Point(*, 100) size))
  return size.ToString();
It will look a little bit more strange with 'unofficial' is operators but still useful
if(box is Rectangle(Point(0,0), Polar(*, 100) size))
  return size.ToString(); //is a Coordinate, not a Polar. 
6.- I like pattern matching and I like record classes, but I don't like that they don't like to play with the rest of C#. Deconstructing types using regular properties, indexers and enumerators is a must to make the feature useful in real world scenarios. Why is only considered for anonymous types and arrays?

I'm sure there is some naiveness in my solution but I don't see it.
if(p1 is { Name = "John"}) // property, matches if p1.Name is "John"
if(p1 is { Name = Length(10)}) // arbitrary pattern, matches if pi.Name is Length(10)
if(p1 is { Name = Length(10) name}) // arbitrary pattern, matches if pi.Name is Length(10) and stores pi.Name in variable  name of type string
if(list is { "john"}) // enumerating values, matches if list.First() is "John", but without throwing exception taking care of the enumerator 
if(list is { [0] = "john"}) // indexer, matches if list[0] is "John", there's no easy way to avoid the IndexOutOfRange, so will be unsafe to use it like this 
if(list is { Length > 1, [0] = "john"}) //matches if list.Lenght > 1 && list[0] is "john", safer version
if(dic is { ["Name"] = "john"}) // indexer, matches if dic["Name"] is "John", no easy way to avoid the KeyNotFoundException without ad-hoc support for ContainsKey or TryGetValue.  
7.- It looks strange that the switch statement has gone from constants to pattern matching without having bool expressions, I definitely vote for something like when suggestion.
switch(point){
   case Point(var x, var y) when x == y: return "diagonal";
   case *: return "other"
Aug 8, 2014 at 1:48 AM
Edited Aug 8, 2014 at 1:48 AM
I noticed in the switch proposal "The order in which patterns are matched is not defined." It would be useful if the order were defined (as in F#) so that we could write more precise patterns before more general patterns:
// assume Cat derives from Animal
switch (obj) {
  case Cat("Slats"): ...
  case Cat(var name): ...
  case Animal(var species): ...
}
Lack of defined ordering would require me to write the Cat logic within the Animal case, as I couldn't be sure that the Cat cases would be checked first. It would also create versioning woes if I got lucky initially, then took a service pack of the framework which subtly changed the compilation strategy in a way which resulted in a different order of matching.
Developer
Aug 8, 2014 at 2:50 AM
itowlson wrote:
I noticed in the switch proposal "The order in which patterns are matched is not defined." It would be useful if the order were defined (as in F#) so that we could write more precise patterns before more general patterns:
The semantics of the switch statement is that the first matched pattern is the one selected. What is not specified is the order in which the match operators are executed.
Developer
Aug 8, 2014 at 5:34 AM
Edited Aug 8, 2014 at 5:36 AM
Olmo wrote:
1.- Whats the benefit of making record classes invocable? (1.2)
The answer is that it creates a parallel between composition (constuction) and decomposition (pattern matching)
var p = Point(1, 2);
if (p is Point(1, 2)) ...
2.- There will be a way to know if a class is a record class, something like typeof(Cartesian).IsRecord, or is just a pattern (like static classes for example).
Don't know. Tentatively no. Currently there is no value in knowing whether the class was written by hand or by compiler expansion.
3.- Maybe is operators should only be allowed in static classes (maybe excluding the type itself) like extension methods do. I assume extensions methods do that to reduce the search space. It's also odd that 'is' operator will be the only 'extensional' operator.
That prevents simple records classes from working. The reason records work in pattern matching is because of the is operator.

I don't know what you mean by "extensional operator". It isn't an operator in the usual sense as the right-hand-side isn't an expression.
4.- What´s the reason for constant-patterns to be constant, and not just expression patterns? I would like to write if(p1 is p2) instead of if(p1.Equals(p2)) (p1 should be not null) or if(object.Equals(p1,p2)) (little bit too long...)
What does "p is Point(1, 2)" mean? Is that a method call on the right or a pattern? The idea is that patterns are not "evaluated", and have no order of evaluation specified, while expressions do. Another way of looking at it is that expressions are for composing values, and patterns are for decomposing values.
5.- Why not let recursive-pattern also have an identifier:
It does. Read the productions in section 1.5.
6.- I like pattern matching and I like record classes, but I don't like that they don't like to play with the rest of C#. Deconstructing types using regular properties, indexers and enumerators is a must to make the feature useful in real world scenarios. Why is only considered for anonymous types and arrays?
I'm not sure I really understand what you're proposing. What is the advantage over simply using properties directly?
7.- It looks strange that the switch statement has gone from constants to pattern matching without having bool expressions, I definitely vote for something like when suggestion.
Patterns do not contain (run-time computed) expressions. That is necessary to enable generating efficient code for a switch statement. See http://www.cs.tufts.edu/~nr/cs257/archive/norman-ramsey/match.pdf
Aug 8, 2014 at 10:22 AM
nmgafter wrote:
JesperTreetop wrote:
I understand that pattern-like patterns (anything involving "is complex-pattern") require record classes. Will there be a way to enlighten other types too? Anonymous types seem like they should be able to adopt all these features magically (although probably not as magically as to not require recompilation).
We would probably want to do this for anonymous types. Since anonymous types do not cross assembly boundaries, you would not be able to observe the extended capabilities unless you wrote - and compiled - code into the same assembly.
Of course. I forgot that you only use them in the same assembly, so it's not a problem.
But wouldn't it be nice if something like anonymous types could cross assembly boundaries?
I do hope that between record classes and readonly auto properties, the brevity problem of writing simple immutable objects will be solved. I don't really want anonymous types everywhere; most of the time I just want something that's as easy to write.
Putting record classes aside, I imagine this would be useful matching types for e.g. serialization. But let's say you're supposed to match IList<T> - that means you'll start with an object (or something similarly amorphous), and there will have to be some sort of syntax for introducing a new generic type parameter to the switch statement or case. How would something like that be written, or would it simply not be, at the level of this draft spec and prototype?
We looked at it, and the CLR can't efficiently express this. Instead you'd have to match using the base IList (nongeneric) type.
Fair enough.

As an aside, when was the last time the CLR was updated to enable new scenarios? I understand that it's not in the hands of even the entire Roslyn team and I know that LINQ, dynamic and async/await were all added without specific CLR support, but it feels like it's been a good while the CLR was revved and that there's still a lot of things that could be done with the right backing. Not having C# alone drive the CLR is probably a good decision for a neutral multi-language component, but it feels like we're still stuck using bits from 2005 and it's in the way of interesting features.
Aug 8, 2014 at 10:40 AM
nmgafter wrote:
Olmo wrote:
1.- Whats the benefit of making record classes invocable? (1.2)
The answer is that it creates a parallel between composition (constuction) and decomposition (pattern matching)
var p = Point(1, 2);
if (p is Point(1, 2)) ...
See below
2.- There will be a way to know if a class is a record class, something like typeof(Cartesian).IsRecord, or is just a pattern (like static classes for example).
Don't know. Tentatively no. Currently there is no value in knowing whether the class was written by hand or by compiler expansion.
Ok
3.- Maybe is operators should only be allowed in static classes (maybe excluding the type itself) like extension methods do. I assume extensions methods do that to reduce the search space. It's also odd that 'is' operator will be the only 'extensional' operator.
That prevents simple records classes from working. The reason records work in pattern matching is because of the is operator.
That's what I mean by excluding the type itself, maybe is wasn't too clear.
I don't know what you mean by "extensional operator". It isn't an operator in the usual sense as the right-hand-side isn't an expression.
Usually operators are static functions that have to be defined in one of the operand classes. This operator defies this rule (in your Polar example), that's why I called it 'extensional operator'. I think there's a lot of this in the first parameter of the is operator, maybe it makes sense to write:
public static class Polar 
{ 
      public static bool operator is(this Cartesian c, out double R, out double Theta) 
      { 
          R = Math.Sqrt(c.X*c.X + c.Y*c.Y); 
          Theta = Math.Atan2(c.Y, c.X); 
          return c.X != 0 || c.Y != 0;    
       }
}
4.- What´s the reason for constant-patterns to be constant, and not just expression patterns? I would like to write if(p1 is p2) instead of if(p1.Equals(p2)) (p1 should be not null) or if(object.Equals(p1,p2)) (little bit too long...)
What does "p is Point(1, 2)" mean? Is that a method call on the right or a pattern? The idea is that patterns are not "evaluated", and have no order of evaluation specified, while expressions do. Another way of looking at it is that expressions are for composing values, and patterns are for decomposing values.
See below
5.- Why not let recursive-pattern also have an identifier:
It does. Read the productions in section 1.5.
Oh.. its true
6.- I like pattern matching and I like record classes, but I don't like that they don't like to play with the rest of C#. Deconstructing types using regular properties, indexers and enumerators is a must to make the feature useful in real world scenarios. Why is only considered for anonymous types and arrays?
I'm not sure I really understand what you're proposing. What is the advantage over simply using properties directly?
See below
7.- It looks strange that the switch statement has gone from constants to pattern matching without having bool expressions, I definitely vote for something like when suggestion.
Patterns do not contain (run-time computed) expressions. That is necessary to enable generating efficient code for a switch statement. See http://www.cs.tufts.edu/~nr/cs257/archive/norman-ramsey/match.pdf
Answer to 1,4,6,7:

I won't have time to red the paper till Tuesday, but I think I see how the features work together.

In this draft, pattern matching works only for types that have an is operator defined, and only for constants, because is harder to make a super-efficient implementation in other cases (I haven't read the paper and I'm assuming that we're speaking about saving some Property access and method calls, not some huge O(N^2) thing).

I think this is a big restriction. There are zillions of classes already made and if a developer has to choose between an simple if statement and implementing is to use pattern matching will go for ifs most of the time.

I think pattern matching should be designed to work also with normal types and arbitrary expressions, not just with records and constants. Maybe with a not-so-nice syntax but there should be an option.

Not having that is like designing LINQ for a new... Sequence<T>, ignoring IEnumerable<T>.

I don't know F#, but what is the expressiveness that they have? Can they use pattern matching with a System.Expression today for example? http://msdn.microsoft.com/en-us/library/dd547125.aspx

I think the draft should be able to do something like this
var list = new List<Fruit>{ 
      new Apple{ Color = "Red" }, 
      new Apple {Color = "Brown"}, 
      new Banana {Color = "Yellor"}, 
      new Orange {Color="Green"}, 
      new Grapes(Enumerable.Range(0,20).Select(c=>new Grape{ Color = "Red" })
};

list.All(IsGood)

bool IsGood(Fruit fruit)
{
     switch(fruits)
     {
            case Orange { Color = "Orange"  }: return true;
            case Apple { Color = "Red"  }: return true;
            case Banana { Color = "Yellow"  }: return true;
            case Grape { Color = "Green"  }: return true;
            case Grapes gs where gs.All(IsGood): return true;
            case * : return true; 
      }
}
Useful pattern matching for collections without a default functional immutable list and tail rec is more challenging however.
Aug 8, 2014 at 11:43 AM
Olmo wrote:
I don't know F#, but what is the expressiveness that they have? Can they use pattern matching with a System.Expression today for example? http://msdn.microsoft.com/en-us/library/dd547125.aspx
You can do it by using Active Patterns, but I couldn't find a library that already does it.

I believe Active Patterns are very similar to what you call "unofficial" is.
Aug 8, 2014 at 2:20 PM
Edited Aug 8, 2014 at 2:24 PM
svick wrote:
Olmo wrote:
I don't know F#, but what is the expressiveness that they have? Can they use pattern matching with a System.Expression today for example? http://msdn.microsoft.com/en-us/library/dd547125.aspx
You can do it by using Active Patterns, but I couldn't find a library that already does it.

I believe Active Patterns are very similar to what you call "unofficial" is.
F# supports LINQ Expressions along with it's own quoting mechanism called Code Quotations which are particularly easy to match over. I've used F# pattern matching over both Code Quotations and LINQ Expressions for a mocking framework, see: http://foq.codeplex.com/SourceControl/latest#Foq/Foq.Linq.fs

In F# you can match arbitrary class types directly using the :? operator and use a when clause for guards, e.g.
    let rec resolve : Expression -> Arg = function
        | :? ConstantExpression as constant ->
            Arg(constant.Value)
        | :? UnaryExpression as unary ->
            unary.Operand |> resolve
        | :? MethodCallExpression as call when isWildcard call.Method ->
            Any
Note: simple pattern matching of classes, including a when clause, is also expected to be part of vb vNext, see: http://trelford.com/blog/post/CaseForVB.aspx
Aug 8, 2014 at 2:23 PM
svick wrote:
Olmo wrote:
I don't know F#, but what is the expressiveness that they have? Can they use pattern matching with a System.Expression today for example? http://msdn.microsoft.com/en-us/library/dd547125.aspx
You can do it by using Active Patterns, but I couldn't find a library that already does it.

I believe Active Patterns are very similar to what you call "unofficial" is.
Not a good indicator that they can not match arbitrary classes. Still their record pattern can match just some properties and not others... I'm sure I'm being naive somewhere but I don't see where.

What is the problem with matching arbitrary classes instead of just records?
Aug 8, 2014 at 2:42 PM
Not a good indicator that they can not match arbitrary classes. Still their record pattern can match just some properties and not others... I'm sure I'm being naive somewhere but I don't see where.

What is the problem with matching arbitrary classes instead of just records?
You can just add an is operator to whatever class you want to match on.

The only thing that makes a record special is that the compiler generates that operator for it.
Aug 8, 2014 at 3:30 PM
mirhagk wrote:
Not a good indicator that they can not match arbitrary classes. Still their record pattern can match just some properties and not others... I'm sure I'm being naive somewhere but I don't see where.

What is the problem with matching arbitrary classes instead of just records?
You can just add an is operator to whatever class you want to match on.

The only thing that makes a record special is that the compiler generates that operator for it.
I know but that will restrict pattern matching for the classes that are designed to match.

If you'll have to call ToSequence method for any single collection you want to use LINQ it won't be so popular.
Aug 8, 2014 at 3:32 PM
ptrelford wrote:
svick wrote:
Olmo wrote:
I don't know F#, but what is the expressiveness that they have? Can they use pattern matching with a System.Expression today for example? http://msdn.microsoft.com/en-us/library/dd547125.aspx
You can do it by using Active Patterns, but I couldn't find a library that already does it.

I believe Active Patterns are very similar to what you call "unofficial" is.
F# supports LINQ Expressions along with it's own quoting mechanism called Code Quotations which are particularly easy to match over. I've used F# pattern matching over both Code Quotations and LINQ Expressions for a mocking framework, see: http://foq.codeplex.com/SourceControl/latest#Foq/Foq.Linq.fs

In F# you can match arbitrary class types directly using the :? operator and use a when clause for guards, e.g.
    let rec resolve : Expression -> Arg = function
        | :? ConstantExpression as constant ->
            Arg(constant.Value)
        | :? UnaryExpression as unary ->
            unary.Operand |> resolve
        | :? MethodCallExpression as call when isWildcard call.Method ->
            Any
Note: simple pattern matching of classes, including a when clause, is also expected to be part of vb vNext, see: http://trelford.com/blog/post/CaseForVB.aspx
Maybe type casting and when clause is enough... but less than that will be a too restricted solution.
Aug 8, 2014 at 4:13 PM
Olmo wrote:
I know but that will restrict pattern matching for the classes that are designed to match.

If you'll have to call ToSequence method for any single collection you want to use LINQ it won't be so popular.
A more appropriate analogy would be that you have to make any collection implement ISequence or something. You do it once per class you want to use it, not once per time you use it.

The team will have to add the is operator to every BCL class that they want to enable pattern matching on.

Of course it can also be done with static extension-like classes, so all they have to do is have a library that has all of the operators defined for all of the base classes.

We could try and define a rule for determining matching arbitrary classes that don't support is, but honestly I'd much prefer have the definite, easy rule of just look for the is operator.
Aug 8, 2014 at 4:52 PM
Edited Aug 8, 2014 at 4:53 PM
mirhagk wrote:
Olmo wrote:
I know but that will restrict pattern matching for the classes that are designed to match.

If you'll have to call ToSequence method for any single collection you want to use LINQ it won't be so popular.
A more appropriate analogy would be that you have to make any collection implement ISequence or something. You do it once per class you want to use it, not once per time you use it.
I considered to use this analogy too, but it's also misleading. Implementing ISequence will be easy because there are just a handful collections (just List<T>, Array, Dictionary<K,V>, Collection<T> will cover most of the cases) while there are zillions of classes containing a bunch of properties out there that will require 'is' to be implemented manually.

Most of this classes have 10s or even hundreds of properties. You can not 'deconstruct' a WPF window... what is the argument number 212?
The team will have to add the is operator to every BCL class that they want to enable pattern matching on.

Of course it can also be done with static extension-like classes, so all they have to do is have a library that has all of the operators defined for all of the base classes.

We could try and define a rule for determining matching arbitrary classes that don't support is, but honestly I'd much prefer have the definite, easy rule of just look for the is operator.
I don't see anything confusing about deconstructing an arbitrary class using the same syntax than for object initializer. F# does exactly that for records. I would like to know why such limitation however....
window.FindControl(c=>c is Border { Child = ComboBox { Name = "bla", Orientation = Orientation.Left}}) //Isn't this beautiful ? 
Aug 8, 2014 at 5:15 PM
Pattern matching as part of overload resolution?

Will we be able to use pattern matching as part of parameter type declarations and let the compiler (runtime?) choose the "matching" overload of a method?

Something like this:
    int Eval(Add (a,b)) {
        return a + b;
    }   
    
    int Eval(Mul (a,b)) {
        return a * b;
    }
    
    int Eval(Div (a,b) when b!=0) {
        return a / b;
    }
Developer
Aug 8, 2014 at 5:48 PM
johnrusk wrote:
nmgafter wrote:
All of that is right. It just requires an overloaded operator is.
So what if we didn't have record classes then? What if we could just get a compiler-generated "is" by asking for it, on any class, with some sort of compact syntax. E.g.
Public class Foo(int Bar)
is;
What would the compiler-generated operator "is" do? Bar isn't an instance field or property in this case, so it isn't clear where it is to get a value for Bar. Can you show me the code you imagine it generating?
Developer
Aug 8, 2014 at 5:50 PM
Olmo wrote:
I think this is a big restriction. There are zillions of classes already made and if a developer has to choose between an simple if statement and implementing is to use pattern matching will go for ifs most of the time.
And that's a good thing.
I think pattern matching should be designed to work also with normal types and arbitrary expressions, not just with records and constants. Maybe with a not-so-nice syntax but there should be an option.
Why?
Developer
Aug 8, 2014 at 5:57 PM
Olmo wrote:
window.FindControl(c=>c is Border { Child = ComboBox { Name = "bla", Orientation = Orientation.Left}}) //Isn't this beautiful ? 
This does look slightly better than:
window.FindControl(c=>c is Border b && b.Child is ComboBox c && c.Name == "bla" && c.Orientation == Orientation.Left) //Isn't this beautiful ? 
But it seems a small improvement compared to the specified complex patterns.
Aug 8, 2014 at 7:20 PM
nmgafter wrote:
Olmo wrote:
window.FindControl(c=>c is Border { Child = ComboBox { Name = "bla", Orientation = Orientation.Left}}) //Isn't this beautiful ? 
This does look slightly better than:
window.FindControl(c=>c is Border b && b.Child is ComboBox c && c.Name == "bla" && c.Orientation == Orientation.Left) //Isn't this beautiful ? 
But it seems a small improvement compared to the specified complex patterns.
Well, is right that the new 'is' will make it it already much better than I'm used:
window.FindControl(c=>(var b == c as Border) !=null && (var c = b.Child as ComboBox) != null && c.Name == "bla" && c.Orientation == Orientation.Left)
Or currently
window.FindControl(c=>c is Border && ((Border)b).Child is ComboBox) && ((ComboBox)((Border)b).Child).Name == "bla" && ((ComboBox)((Border)b).Child).Orientation == Orientation.Left)
Still I don't know why is my proposal so complex. Syntactically will be just:
complex-pattern: 
    constant-pattern 
    type identifier 
    recursive-pattern 
    recursive-pattern identifier
    property-init-pattern <--------- from here

property-init-pattern
    { property-pattern-list }   
 
property-pattern-list
     property-pattern
     property-pattern-list, propert-pattern

property-pattern
     ident = complex-pattern <--------- to here

recursive-pattern: 
    type ( subpattern-listopt )

subpattern-list: 
     subpattern 
     subpattern-list , subpattern

subpattern: 
     argument-nameopt pattern

pattern: 
     simple-pattern 
     complex-pattern

simple-pattern: 
     wildcard-pattern 
     var identifier

wildcard-pattern: 
     *

constant-pattern: 
    constant-expression
With just this, pattern-matching will work for all the classes, not just record classes or classes with the is operator (currently 0%).

I'm sure there is a deep technical reason for the complexity that I don't see.

Aditionally, you solution wont work in switch statements because there's also no when. Am I right?
swith(c)
{
  case Border b:
  {     
       if(b.Child is ComboBox c && c.Name == "bla" && c.Orientation == Orientation.Left)
            //...
   } 
}
Well.. it doesn't look that bad but all the merit goes to Type ident.

With the current draft:
  • Type ident will be king, is a fantastic syntax (but for nullables or future non-nullable reference types)
  • Records will be the queen, as an alternative to tuples and anonymous types.
  • Pattern matching and 'operator is' will be a niche thing for people writing a parser / evaluator / symbolic calculator and not using F# in the first place.
I'm not even sure if it will work with most of the classes in System.Expression without support for ReadOnlyCollection<T> and the constant-expression restriction
Experession<Func<string, string>> s => s.Substring(1,2);
switch(s.Body)
{
     case MethodCallExpression(var str, MethodName("Substring??"),  ??arguments??): 
}
I would like to write:
switch(s.Body)
{
     case MethodCallExpression { Sender = var str, Method = { Name = "Substring" }, Arguments = { var index, var lenght }): 
}
Aug 8, 2014 at 10:01 PM
Edited Aug 8, 2014 at 10:07 PM
Awesome work on the proposal! I think the proposal it's really well formed, and definitely has all the best elements from the original proposal plus the feedback people have given on CodePlex. This has all the elements of being the killer feature for C# vNext. Really looking forward to trying a prototype.

I had just two comments:
  • I'm cautious about the syntax eliding new. I think doing that makes it harder at a glance to tell that you're performing an allocation. However, I think this problem could be fixed by building an allocation-identifying analyzer directly into Visual Studio (similar to the one R# has produced recently).
  • Has the team given any thought on a "mutating" syntax for these immutable objects? That seems like the biggest missing piece to me (unless I'm missing an important use case in the proposal). For example, I have:
var p = new Point(3,4);
and now I want to subtract 1 from the x-axis. There has got to be something better that can be done at the language level other than
p = new Point(p.X - 1, p.Y); 
This quickly grows out of control for more complicated types.
Developer
Aug 8, 2014 at 10:13 PM
MgSam wrote:
Awesome work on the proposal! I think the proposal it's really well formed, and definitely has all the best elements from the original proposal plus the feedback people have given on CodePlex.
The only change so far has been to rename "pattern class" to "record class".
  • Has the team given any thought on a "mutating" syntax for these immutable objects? That seems like the biggest missing piece to me (unless I'm missing an important use case in the proposal). For example, I have:
var p = new Point(3,4);
and now I want to subtract 1 from the x-axis. There has got to be something better that can be done at the language level other than
p = new Point(p.X - 1, p.Y); 
This quickly grows out of control for more complicated types.
You can make anything you want mutable (this is already in the spec). They just aren't mutable by default. Using features planned for C#6, you'd be able to write
record class Point(int X, int Y)
{
  public int X { get; set; } = X;
  public int Y { get; set; } = Y;
}
Aug 8, 2014 at 10:30 PM
Edited Aug 8, 2014 at 10:31 PM
nmgafter wrote:
You can make anything you want mutable (this is already in the spec). They just aren't mutable by default.
I don't think that is what MgSam had in mind. So let me rephrase the question: Will there be a C# equivalent of F#'s update record feature? For instance:
type MyRecord = { A : int; B : int; C : int } // defines an immutable record type
let x1 = { A = 1; B = 2; C = 3 } // creates an instance of the record
let x2 = { x1 with B = 16 } // makes a copy of x1, only changing B to 16, copying all other property values
printf "%A" x2 // prints something like "A = 1, B = 16, C = 3"
With your proposal, it is possible to translate the F# code above to C# as follows:
record class MyRecord(int A, int B, int C); // defines an immutable record class
// Within some method
var x1 = new MyRecord(1, 2, 3); // creates an instance of the record class
var x2 = new MyRecord(x1.A, 16, x1.C); // is there a shorter way to make a copy with an updated value for B?
Console.WriteLine(x2) // prints something like "A = 1, B = 16, C = 3"
Which is actually quite nice, however, F#'s with syntax is clearly missing here, forcing us to explicitly mention properties A and C again when performing the copy. This gets tedious fast for records with large amounts of properties.

You mentioned in the proposal that most of Roslyn might benefit from using record classes and pattern matching; basically you could probably completely replace the generated syntax/bound nodes hierarchies that you currently have with actual, handwritten C# code. With the current proposal, however, you'd get only half way there, as you'd still have to write all those boilerplate With and Update methods. F#'s with syntax to copy-and-update records solves that problem in quite an elegant way. So I definitely think that something similar is needed for C# as well.
Developer
Aug 8, 2014 at 10:39 PM
Expandable wrote:
Will there be a C# equivalent of F#'s update record feature?
As for "will", there hasn't been a decision, nor much discussion, of this proposal in the language design committees. At this point we're just floating an idea.

Having said that, I think an equivalent of F#'s update record would be valuable.
record class MyRecord(int A, int B, int C);

// Within some method
var x1 = new MyRecord(1, 2, 3);
var x2 = x1 with B: 16;
Console.WriteLine(x2) // prints something like "A = 1, B = 16, C = 3"
Aug 8, 2014 at 10:49 PM
Edited Aug 8, 2014 at 10:53 PM
nmgafter wrote:
Expandable wrote:
Will there be a C# equivalent of F#'s update record feature?
As for "will", there hasn't been a decision, nor much discussion, of this proposal in the language design committees. At this point we're just floating an idea.
Sorry, I know that, I meant "will" in the sense of "have you considered".

nmgafter wrote:
Having said that, I think an equivalent of F#'s update record would be valuable.
Good to hear that. with would be a new keyword, however. I have the impression that the C# language team tries hard to avoid that. It would have to be a contextual keyword anyway, obviously. Checking the list of keywords that could potentially be reused for that purpose, I think only set might be ok, though clearly inferior to with from an "easy-to-understand-and-guess-its-semantics" point of view.
var x2 = x1 set B : 16;
Or maybe better, to reuse some standard C# syntax:
var x2 = x1 set { B = 16 }; 
var x2 = x1 with { B = 16 }; 
var x2 = x1 { B = 16 }; // maybe no keyword needed at all?
I'd actually prefer the one without any keyword, as it would closely resemble the current object initialization syntax. And it could even be extended to non-record types, to allow setting properties without having to specify the object name every time. But that's a different discussion.
Aug 9, 2014 at 8:38 AM
Continuing my crusade for more powerful pattern matching, I've read the paper and I can see that:
  • A decision tree is created to minimize comparisons in a sequence of matches
  • heuristics can be included to create better decision trees, but the differences are around +-5%
I can not see why expression evaluation can not be included in the decision tree, so I think the challenges are bigger in solving syntactic ambiguities.

I also haven't found a reason why a pattern similar 'record pattern' in f# could be implemented.

potential side effects could be visible when accessing properties, or enumerating sequences, that will change depending the order but the same is true if equals or is operator produces side effects.
Aug 9, 2014 at 2:09 PM
Expandable wrote:
I don't think that is what MgSam had in mind. So let me rephrase the question: Will there be a C# equivalent of F#'s update record feature?
Yes, this is correct.

I think a with contextual keyword is fine. I believe the team's aversion is to adding new reserved words. I don't see why reusing existing English words for a new feature is preferable to picking different ones, as long as the feature is sufficiently different. I'd imagine with would have a corresponding operator definition similar to the proposal for is.
Aug 9, 2014 at 7:16 PM
Consider the introduction of a method level case construct that leverages partial classes and extension methods to “solve” the well known “Expression Problem of Wadler” . So we can rewrite the expresion example across mulitpule files in a more modular way. Note this does not technically solve Wadler’s problem as he wanted a binary compatible approach and the below is just a compile time convience to let you organize the source in a more modular way.
// Expr.cs
abstract class Expr;
partial static class ExprExtensions
{
  public Expr Simplify(this Expr e default) { return e; } 
}

// VarExpr.cs
record class X() : Expr;

// ConstExpr.cs
record class Const(double Value) : Expr;

// AddExpr.cs
record class Add(Expr Left, Expr Right) : Expr;
partial static class ExprExtensions {
  static public Expr Simplify(this Expr e case Add(Const(0), var x)) { return Simplify(x); }
  static public Expr Simplify(this Expr e case Add(var x, Const(0)) { return Simplify(x); }
  static public Expr Simplify(this Expr e case Add(Const(var l), Const(var r)) { return Simplify(x); }
}

// MultExpr.cs
record class Mult(Expr Left, Expr Right) : Expr;
partial static class ExprExtensions {
  static public Expr Simplify(this Expr e case Mult(Const(0),*)) { return Const(0); }
  static public Expr Simplify(this Expr e case Mult(*,Cost(0)))  { return Const(0); }
  static public Expr Simplify(this Expr e case Mult(Const(1), var x)) { return Simplify(x); }
  static public Expr Simplify(this Expr e case Mult(var x, Const(1))) { return Simplify(x); }
  static public Expr Simplify(this Expr e case Mult(Const(var l), const(var r)) { return Const(l*r); }
}

// NegExpr.cs
record class Neg(Expr Value) : Expr;
partial static class ExprExtensions {
static public Expr Simplify(this Expr e case Neg(Const(var k)) { return const(-k); }
}
Note you can generalize the "is" syntax to give you a form of multi-dispatch as well.
// SpaceObject
abstract class SpaceObject;
record class Asteriod(...) : SpaceObject;
record class SpaceShip(...) : SpaceObject;

CollideWith(SpaceObject o1 is Asteriod a, SpaceObject o2 is SpaceShip s1) {

}
Aug 9, 2014 at 7:22 PM
nmgafter wrote:
itowlson wrote:
I noticed in the switch proposal "The order in which patterns are matched is not defined." It would be useful if the order were defined (as in F#) so that we could write more precise patterns before more general patterns:
The semantics of the switch statement is that the first matched pattern is the one selected. What is not specified is the order in which the match operators are executed.
The above semantics are were not clear to me when I read, the spec, can there be a note to clarify the spec?
Aug 9, 2014 at 7:25 PM
plgeek wrote:
Consider the introduction of a method level case construct that leverages partial classes and extension methods to “solve” the well known “Expression Problem of Wadler” . So we can rewrite the expresion example across mulitpule files in a more modular way. Note this does not technically solve Wadler’s problem as he wanted a binary compatible approach and the below is just a compile time convience to let you organize the source in a more modular way.
...
I just noticed the order of pattern matching does matter. This would mean my proposal above would require a restriction that matches are non-over lapping so it is not a simple syntactic transformation, but would require a bit more verification at compile time, while that adds some complexity, I think it adds a lot of value as it really makes modular authoring of transformations much more tractable.
Developer
Aug 9, 2014 at 8:56 PM
plgeek wrote:
nmgafter wrote:
itowlson wrote:
I noticed in the switch proposal "The order in which patterns are matched is not defined." It would be useful if the order were defined (as in F#) so that we could write more precise patterns before more general patterns:
The semantics of the switch statement is that the first matched pattern is the one selected. What is not specified is the order in which the match operators are executed.
The above semantics are were not clear to me when I read, the spec, can there be a note to clarify the spec?
Please see section 1.6. Can you please suggest how it could be clarified? It says

The switch statement is extended to select for execution the first block having an associated pattern that matches the switch expression...

The order in which patterns are matched is not defined. A compiler is permitted to match patterns out of order, and to reuse the results of already matched patterns in matching of other patterns.
Aug 9, 2014 at 10:24 PM
nmgafter wrote:
Please see section 1.6. Can you please suggest how it could be clarified? It says

The switch statement is extended to select for execution the first block having an associated pattern that matches the switch expression...

The order in which patterns are matched is not defined. A compiler is permitted to match patterns out of order, and to reuse the results of already matched patterns in matching of other patterns.
It would be good to explicitly teases apart the difference between "meaning of switch when there are overlapping matches" vs "order of evaluation of destruction operators". The are subtlety different, and I got confused because I incorrectly read "the order in which patterns are match is not defined" as the "patterns should not overlap". Suggested exposition is below, I'm sure it could be improved...

There are two issues in defining the semantics of switch, the code block that is selected when patterns overlap, the other is the order of evaluation of "is" and other destruction operators and equality operators that are used make the pattern matching decisions at runtime.
The example below helps illustrate the first issue related to pattern overlap.
 switch(e) {
  case Mult(*,Const(1)): .. block1;
  case Mult(Const(1),*): .. block2;
}
In the above example when e == Mult(Const(1),Const(1)) both patterns match, in this case the match compiler will always select block1 in case of overlapping matches, because it is first in the order. Therefore unlike normal switch case semantics, the order of case statements matter. In the standard switch case statements there is no ambiguity because there normally is no way for case clauses to overlap.

In order to implement the match, the compiler is free to call "is" and other destruction operations and equality operators in any order. It is not required to call any "is" operator if it can determine either statically or at runtime that calling that destruction operator is not needed to determine the correct code block to execute.
Developer
Aug 10, 2014 at 12:36 AM
May I have your permission to use that text in the spec?
Aug 10, 2014 at 5:27 PM
Sure.
Aug 10, 2014 at 6:10 PM
BTW on my list idioms I miss in my day to day programming in C# that are easy to do in functional programming languages is the combination of tuples and pattern matching as a way to handle complicated decision logic. I don't know how this interacts with existing tuple proposals, but let me suggest adding "virtual pattern tuples" for swith. So the following is a valid
bool x = ..
bool y = ..
switch (x, y)
{
  case (true,true) : ...
  case (true,false) : ...
  case (false,_) : ...
}

This lets me let the compiler figure out the most efficient way to compile the code to a bunch if tests without sacrificing readability, and it will catch when I screw up the cases analysis and have dead cases. Of course this is not limited to bools but also to other types. The match compiler already has to deal with this scenario internally so it can handle nested patterns like the below correctly.
 switch(e) {
  case Mult(*,Const(1)): ...
  case Mult(Const(1),*): ...
}
It's a very small extension that will really improve the value of pattern matching. I think the only down side is I'm not sure how this interacts with various outstanding tuple extensions.
Developer
Aug 10, 2014 at 11:38 PM
I would like to add "value tuples" at the same time as records and pattern matching. That would enable exactly what you describe. I think that makes more sense than adding support for tuples just in the context of the switch statement.
Aug 11, 2014 at 2:15 AM
Edited Aug 11, 2014 at 4:09 AM
I am very much interested in this topic, so here we go!

First up, I saw no mention in the spec of patterns being combinable with &&. Is there any trouble with this? F# already supports this in match blocks.

Next, regarding the when clause in the switch statements, I can't imagine how one could work around their absence. Is there some way to tell the switch to continue looking for matches after the program has entered one of the branches?

Someone mentioned using var rather than *. This actually seems kinda intuitive to me.
switch(x) {
  case int y:  // x must be an int.  Value is bound to y.
  case int:    // x must be an int.
  case var y:  // x can be anything.  Value is bound to y.
  case var:    // x can be anything.
}
I also liked the idea of replacing case with is, but that's really not a big deal.

I forsee a slight problem when we come to very large record classes. If someone makes a class with 10 fields, you aren't going to want to write x is SomeRecord(*, *, *, *, *, *, *, int x, *, *) My first thought was to allow named arguments, and automatically fill in unspecified positions with *, but then a better idea hit me. How about enabling us to match any readable property using named args? Matching support for existing types would then just light up automatically. Also, Olmo would be happy! Besides, the idea of mapping named args to properties already exists with attribute declaration.
if(box is Rectangle(Left := 0, Width := var w))

window.FindControl(c => c is Border(Child := ComboBox(Name := "bla",
                                                      Orientation := Orientation.Left)))
The is operator overloads can then be dedicated to positional usage.

Finally, are these new C# features only going to be available when targeting the next version of .Net, or will there be tricks to make them compatible with existing versions? For instance, Microsoft eventually released some packages that enable async to be used with .Net 4.0.
Developer
Aug 11, 2014 at 4:37 AM
Edited Aug 11, 2014 at 4:45 AM
@Yota: All great ideas.

The reason for using "case" for the switch statement is the same as the reason it is used there today. The existing switch construct is a special case of the generalized construct; it would not be reasonable to expect everyone to change "case" to "is" in their existing switch statements, and the expansion of the set of patterns supported is not a reason to change the syntax.

I particularly like your suggestion to use readable properties in the absence of an "operator is". I wonder how it should work if there is an "operator is". Are you suggesting named arguments should not be supported when using an "operator is"? The spec already supports
if (p is Point(x: 1, y: 2)) ...
If named arguments are supported there, how does the language decide when to use "operator is" and when to use properties directly?

I recently modified the spec to support separate specification of the parameter and property names:
class Point(int x: X, int y: Y);
The parameter names "x" and "y" are used for the parameters of the constructor and "operator is", while the names "X" and "Y" are used for the properties. In this case the keyword notation for pattern matching would use the (lower case, in this case) parameter names. But if we extend pattern matching to support readable properties, we probably want the keyword parameters in a pattern to use the property names.
Aug 11, 2014 at 11:01 AM
Hey! Nice that matching properties is taking some momentum!
The parameter names "x" and "y" are used for the parameters of the constructor and "operator is", while the names "X" and "Y" are used for the properties. In this case the keyword notation for pattern matching would use the (lower case, in this case) parameter names. But if we extend pattern matching to support readable properties, we probably want the keyword parameters in a pattern to use the property names.
I really think it makes more to take advantage of the following symmetry:
  • constructor-deconstruction like proposed right now for matching with the is operator.
var p = new Point(1,2);
switch(p){
   case Point(1, var y): return true;
   case Point(var x, *): return false;
}
  • object initializing-deinitializing like I proposed for reading properties
var p = new Point { X = 1, Y = 2);
switch(p){
   case Point { X = 1,  Y = var y }: //[...]
   case Point { X = var x, Y = *}: //[...]
}
  • collection initializing-deinitializing like I proposed for reading the enumerator
var p = new List<int> { 1, 2, 3);
switch(p){
   case List<int>{ 1,  3 }: return true; //  exactly 2 elements
   case List<int>{ 1,  var v2, ... }: return true; // >2 elements
   case List<int>{ ... }: return false; // > 0 elements
}
Maybe we can replace ... by another token like ** or params.

Dictionaries are more tricky, but whats the reason to discard this simple, consistent and C#-ish alternative?
Aug 11, 2014 at 11:10 AM
And just like constructors and object initializers, it can be combined gracefully:
HtmlElement element = new HtmlElement(type: "a", id = "myContainer") { Href = "https://roslyn.codeplex.com" };

switch(element){
    case HtmlElement(type: "a", id: var id) { Href = var href }: return string.Format("{0} refers to {1}", id, href); 
}
Aug 11, 2014 at 4:15 PM
The initializer syntax that Olmo has been suggesting is certainly a lot more appealing. It allows us to match the indexer, using the new [...] and $... syntax. The distinct context enables Intellisense to list properties on the given type as well. Oh, and it enables the matching of dynamic objects, which could potentially be of huge benefit. (In this case, the spec should clarify what happens if a member is not found on the dynamic type. Failure or exception? I'd probably go with failure, as this will likely be an expected scenario.)

Should this syntax require that a type be specified? Can the type be inferred with var? Should we be able to omit the type completely, as per Olmo's very first example?

Most importantly, however, can it be done? Considering where they can be used, I can't think of any issues off hand.
Aug 11, 2014 at 4:55 PM
One VB.NET feature which IMHO had been missing from C# was a non-overloadable Is operator which tests exclusively for reference equality (e.g. in VB.NET, If Ref1 Is Ref2 Then ... will perform a reference-equality test even if the references are of a type like String which overloads the equality-test operator. If code is not expecting that a reference type overloads the equality-test operator, and wouldn't want to use the overload if it existed, it would be better to a more concise syntax than Object.ReferenceEquals(Ref1, Ref2) to indicate that. An is operator would seem like a natural fit, but not if it gets used for pattern matching.

I would think that integration with vb.net might be cleanest if is was used for reference equality and something like is like was used for pattern-matching (pattern matching could probably be added to VB.NET without ambiguity, using the already-existing Like keyword in that language). I would consider it unfortunate if there were situations in which Is would be legal in both VB.NET and C#, but meant completely different things [in VB.NET, someObject Is somePattern would test whether someObject identified the same object as somePattern].
Developer
Aug 11, 2014 at 5:09 PM
@Olmo: Brilliant! May I have permission to use your ideas in the specification?

@supercat: In the draft spec for pattern matching in VB.Net (not published yet, sorry) we use "Matches" instead of "Is". We already have "is" in both languages - in C# it is a type test, and in VB it is reference identity.
Aug 11, 2014 at 6:58 PM
Is there any chance of switch becoming an expression? I. e. something like:
var res = switch (element)
{
     case HtmlElement(type: "a", id: var id): return id;
     default: return "none"
}
or even better
var res = switch (element)
{
     case HtmlElement(type: "a", id: var id) => id
     default => "none"
}
Developer
Aug 11, 2014 at 7:24 PM
Switch as an expression is fairly orthogonal to most of this record/pattern stuff, though clearly there is significant synergy. I don't want to fold that into the current specification for fear of complicating the initial internal discussion about these features.
Aug 11, 2014 at 8:05 PM
nmgafter wrote:
@Olmo: Brilliant! May I have permission to use your ideas in the specification?
Of course!
@supercat: In the draft spec for pattern matching in VB.Net (not published yet, sorry) we use "Matches" instead of "Is". We already have "is" in both languages - in C# it is a type test, and in VB it is reference identity.
Aug 12, 2014 at 9:11 AM
nmgafter wrote:
johnrusk wrote:
nmgafter wrote:
All of that is right. It just requires an overloaded operator is.
So what if we didn't have record classes then? What if we could just get a compiler-generated "is" by asking for it, on any class, with some sort of compact syntax. E.g.
Public class Foo(int Bar)
is;
What would the compiler-generated operator "is" do? Bar isn't an instance field or property in this case, so it isn't clear where it is to get a value for Bar. Can you show me the code you imagine it generating?
Ah, silly me. I forgot to re-check the primary constructor syntax before posting. I assumed that "public class Foo(int Bar)" was equivalent to this C#6 code
public class Foo(int bar)
{
    public int Bar {get;} = bar;
}
Which raises the question, why is the shorter syntax not allowed? In fact, could we go so far as to argue that the proposed record classes are a workaround to the verbosity of the proposed C#6 approach? Why not allow a variation of what Damien Guard suggested on his blog, like this
public class Foo(get int Bar);
The idea being that the word "get" shows that a read only property should be created and initialized to the value of the parameter.

I know that the bar has always been set very high for the introduction of new features in C#. And yet, when we look at primary constructors and record classes, we see two new features... when it appears that one might do.

Incidentally, the above syntax would support the two remaining "features" of record classes as follows:
  • Automatically generate an "is" operator with parameters that correspond to those of the primary constructor - if that primary constructor uses "get" to signal that it's parameters map to properties. This is consistent with Olmo's point about the construction-deconstruction symmetry, because the automatically generated deconstructor (the "is") would be automatically generated from the primary constructor.
  • Automatically make a class invokable by its primary constructor. (I.e. allow omitting "new" operator when calling the primary constructor)
I feel this proposal, combined with Olmo's, would make the language feel more cohesive. It would also put an end to the inevitable questions asking how record classes are different from "normal" classes.
Developer
Aug 12, 2014 at 3:21 PM
Edited Aug 12, 2014 at 4:26 PM
@johnrusk All excellent questions. I hope my colleagues who are on the C# language design committee will have an answer. The reason that there is a "record" modifier in the current specification is to avoid conflict with a syntax already chosen in C# 6 to mean something different (primary constructors).
Aug 12, 2014 at 5:04 PM
Olmo wrote:
nmgafter wrote:
@Olmo: Brilliant! May I have permission to use your ideas in the specification?
Of course!
@supercat: In the draft spec for pattern matching in VB.Net (not published yet, sorry) we use "Matches" instead of "Is". We already have "is" in both languages - in C# it is a type test, and in VB it is reference identity.
At present, the uses of Is in the two languages do not overlap in the absence of identically-spelled type and member identifiers. Given:
class Evil {};
class Nasty
{
  System.Object Evil;
  void blah(Object x)
  {
    if (x is Evil)
      doSomething();
  }
The VB.Net equivalent would be If TypeOf(x) Is Evil Then doSomething(), and If x Is Evil Then doSomething() would be syntactically legal but different; in most cases, however, the C# is would be used only with compiler types, and the VB.NET Is only used with either references or TypeOf expressions. The possibility of code like the above would probably prevent C# from using is directly as a reference-comparison operator (which is, IMHO, what it should always have been) but I would still prefer a context-sensitive helper keyword for the pattern-matching case.
Aug 12, 2014 at 7:55 PM
johnrusk wrote:
Ah, silly me. I forgot to re-check the primary constructor syntax before posting. I assumed that "public class Foo(int Bar)" was equivalent to this C#6 code
public class Foo(int bar)
{
    public int Bar {get;} = bar;
}
Which raises the question, why is the shorter syntax not allowed? In fact, could we go so far as to argue that the proposed record classes are a workaround to the verbosity of the proposed C#6 approach? Why not allow a variation of what Damien Guard suggested on his blog, like this
public class Foo(get int Bar);
The idea being that the word "get" shows that a read only property should be created and initialized to the value of the parameter.

I know that the bar has always been set very high for the introduction of new features in C#. And yet, when we look at primary constructors and record classes, we see two new features... when it appears that one might do.

Incidentally, the above syntax would support the two remaining "features" of record classes as follows:
  • Automatically generate an "is" operator with parameters that correspond to those of the primary constructor - if that primary constructor uses "get" to signal that it's parameters map to properties. This is consistent with Olmo's point about the construction-deconstruction symmetry, because the automatically generated deconstructor (the "is") would be automatically generated from the primary constructor.
  • Automatically make a class invokable by its primary constructor. (I.e. allow omitting "new" operator when calling the primary constructor)
I feel this proposal, combined with Olmo's, would make the language feel more cohesive. It would also put an end to the inevitable questions asking how record classes are different from "normal" classes.
This brings up a good point, which is that, as proposed, the record modifier may do too much.

Should the immutability of the class be tied to the single keyword record? Perhaps record should just indicate that you want the compiler to auto-generate Equals, GetHashCode, and ToString and to use the primary constructor parameters as properties? Then if you want an immutable record, you use public readonly class Point(int X, int Y); (note the readonly keyword). It seems like being forced to drop back to:
class Point(int X, int Y)
{
  public int X { get; set; } = X;
  public int Y { get; set; } = Y;
}
for something mutable as is still falling off the cliff of convenience pretty quickly.

The main disadvantage I see to this is that the "easier" option as far as the language is concerned is to still write mutable classes, as immutability requires the extra readonly keyword. An alternative would be that immutability is the default for record classes (as in the current proposal) and that to get a mutable record you add a new mutable keyword. Swift certainly has taken this route.
Aug 12, 2014 at 9:04 PM
nmgafter wrote:
@johnrusk All excellent questions. I hope my colleagues who are on the C# language design committee will have an answer. The reason that there is a "record" modifier in the current specification is to avoid conflict with a syntax already chosen in C# 6 to mean something different (primary constructors).
I'd honestly argue that primary constructors should be dropped/delayed until the pattern matching story can be integrated. As it stands primary constructors just seem like a bizarre secondary syntax to defining a normal class and through all of the churn of attempting feature-parity with normal classes, and horrific Java-esque syntax proposed as a result, I just think it will be confusing and offer no real advantages to how we've always defined classes. This is especially true now that getter-only auto-properties have been promoted to normal class definitions and don't require that whole body-initializer mess.
Aug 13, 2014 at 10:35 AM
This is what primary constructors do:
  • Sorter syntax for a constructor that should always be called
  • Global initialization context to initialize properties.
This is what records add to the table:
  • Auto generate fields and properties.
  • Implement Is Operator
  • Implement Equals
  • Implement GetHashCode
Primary constructor can be mutable, while records are always immutable, In order to fusion the two context could make sense to add the concept of Key.

Key shouldn't change, to create stable Equals and GetHashCode. But it makes perfect sense to auto generate fields and properties, and the Is Operator, for mutable entities, so what about:
public class MyClass(
    public key int Id, 
    public string Name, 
    DateTime? dateOfBirth = null)
{
     public DateTime? DateOfBirth = dateOfBirth; 
}
  • Writing public (or protected, internal, private...) on a primary constructors gives you automatic property and Is Operator.
  • Writing key gives you automatic Equals and GetHashCode.
The reason automatic properties and is should be related is because you can not ensure the constructor argument-property relationship just with primary constructors. You could write a strange type like this
public class Point( int x, int y)
{ 
    public int X {get; } = y;
    public int Y {get; } = x;

    public static operator is(Point point, out int x, out int y)
    {
        x = ???
        y = ???
    }
}
Another topic is whether any immutable property should be part of the key...
Aug 13, 2014 at 10:46 AM
Olmo wrote:
Primary constructor can be mutable, while records are always immutable, ...
No, see nmgafter's answer above:

nmgafter wrote:
You can make anything you want mutable (this is already in the spec). They just aren't mutable by default. Using features planned for C#6, you'd be able to write
record class Point(int X, int Y)
{
  public int X { get; set; } = X;
  public int Y { get; set; } = Y;
}
Aug 13, 2014 at 1:00 PM
Edited Aug 13, 2014 at 1:00 PM
Expandable wrote:
Olmo wrote:
Primary constructor can be mutable, while records are always immutable, ...
No, see nmgafter's answer above:

nmgafter wrote:
You can make anything you want mutable (this is already in the spec). They just aren't mutable by default. Using features planned for C#6, you'd be able to write
record class Point(int X, int Y)
{
  public int X { get; set; } = X;
  public int Y { get; set; } = Y;
}
I skip this part, but the problem stills there.

a mutable record will create unstable GetHashCode. I don't think the framework should promote that. There's a strong relationship between Immutability and GetHashCode
Developer
Aug 13, 2014 at 4:15 PM
Olmo wrote:
a mutable record will create unstable GetHashCode. I don't think the framework should promote that. There's a strong relationship between Immutability and GetHashCode
Agreed: you shouldn't do that. Nevertheless, it is useful to many people who write code that way. They are just careful to do their mutations early, and then stop mutating before using GetHashCode. I'm not saying it should be encouraged. In fact, the spec for records explicitly discourages it by forcing you to go out of the way if you want to make it happen.
Aug 13, 2014 at 5:07 PM
Olmo wrote:
a mutable record will create unstable GetHashCode. I don't think the framework should promote that. There's a strong relationship between Immutability and GetHashCode
True, but you can find many examples of "bad" things that are allowed both by the language and by the framework. Mutable structs being another example. Nevertheless, they are occasionally useful, so the language shouldn't make it possible to use an optimization if you know what you're doing. And even F# allows mutable records. Though, admittedly, F#'s default Map implementation doesn't rely on GetHashCode, so it's not such a big issue there.

Also, this issue is complete irrelevant when you're not using your records with dictionaries or sets or something similar, which is quite often the case. In my current projects, I'd say that less than 1% of the types are ever used as keys of some dictionaries. Sets even less.
Aug 13, 2014 at 5:13 PM
nmgafter wrote:
Olmo wrote:
a mutable record will create unstable GetHashCode. I don't think the framework should promote that. There's a strong relationship between Immutability and GetHashCode
Agreed: you shouldn't do that. Nevertheless, it is useful to many people who write code that way. They are just careful to do their mutations early, and then stop mutating before using GetHashCode. I'm not saying it should be encouraged. In fact, the spec for records explicitly discourages it by forcing you to go out of the way if you want to make it happen.
What .NET really needed to have in order to properly handle equality of mutable items was to either have two sets of equality/hashing operators, or else have a the methods include a "mode" parameter, to indicate whether they should define an equivalence relation which will hold as long as the objects in question exist, or that will hold unless or until either object is modified. A very substantial fraction of immutable objects contain an object of a mutable class which is never exposed to anything that will mutate it. The wrapped object has no way of knowing that it will never be modified, but the code which is wrapping it does know. There are many cases where e.g. code wraps arrays or lists that will never change, and needs to base equivalence on the contents of those arrays or lists. It would have been very helpful if there were a standard means by which types could include methods for performing content-based equality-checks and hashing [note that many types of collections would either need a constructor parameter or something in their type to indicate which form of equality testing should be done on the items within them].

The lack of distinct methods for the two different kinds of equality testing forces a trade-off between reporting an equivalence relation which duplicates reference equality, or reporting an equivalence relation which assumes that the object will not be modified outside the control of whomever is asking about equivalence, thus requiring that code which needs reference equality should test for it explicitly. Neither situation is ideal. If two objects each hold a private object of mutable type that will never be mutated, having the outer objects test equivalence by asking the inner objects if their present state match would be cleaner than requiring the outer objects to manually test every aspect of the inner objects' state.
Aug 13, 2014 at 5:15 PM
Edited Aug 13, 2014 at 5:15 PM
nmgafter, how will the is operator be implemented with this crazy example?
public record class Point( int x, int y)
{ 
    public int X {get; } = y;
    public int Y {get; } = x;
  
    //Autogenerated
    public static operator is(Point point, out int x, out int y)
    {
        x = ???
        y = ???
    }
}
Developer
Aug 13, 2014 at 9:12 PM
Olmo wrote:
nmgafter, how will the is operator be implemented with this crazy example?
public record class Point( int x, int y)
{ 
    public int X {get; } = y;
    public int Y {get; } = x;
  
    //Autogenerated
    public static operator is(Point point, out int x, out int y)
    {
        x = ???
        y = ???
    }
}
It will use your (silly) properties.
Aug 13, 2014 at 10:20 PM
Edited Aug 13, 2014 at 10:21 PM
Mmmm... thinking about it deeper. In my example, would my Uppercase X and Y properties override the lowercase auto-generated x, y properties?

So, imagining I wrote it properly the first time, this will be the generated implementation, right?
public record class Point( int X, int Y)
{ 
    public int X {get; } = Y;
    public int Y {get; } = X;
  
    //Auto-generated
    public static operator is(Point point, out int X, out int Y)
    {
        X = this.X;
        Y = this.Y; 
    }
}
The deconstructor just exposes the properties as defined in the constructor, without analyzing how the value got in there. Right?

Just to clarify.
Developer
Aug 14, 2014 at 12:09 AM
Edited Aug 14, 2014 at 12:14 AM
Olmo wrote:
Mmmm... thinking about it deeper. In my example, would my Uppercase X and Y properties override the lowercase auto-generated x, y properties?
Oh, my mistake, I missed the case difference. The compiler generates lower-case properties and uses those in the original example. Your hand-written X and Y properties are just properties that could have been named anything and have nothing to do with any generated code.

But the current spec does support naming them separately:
public record class Point(
    int x : X, // parameter named x, corresponding property named X
    int y: Y);
You could be silly and write that
public record class Point(
    int x : Y, // parameter named x, corresponding property named Y
    int y: X);
Which would surely confuse the users of this type.
Aug 14, 2014 at 7:02 AM
Olmo wrote:
This is what primary constructors do:
  • Sorter syntax for a constructor that should always be called
  • Global initialization context to initialize properties.
This is what records add to the table:
  • Auto generate fields and properties.
  • Implement Is Operator
  • Implement Equals
  • Implement GetHashCode
Nice list. Makes it clear that "record" is all about auto-generation. Somehow, having the word "record" mean "automatically generate stuff" doesn't smell right to me. There seem to be a number possibilities worth considering :
  1. Replace the word "record" with the word "autogenerate". This makes it clear what the keyword does, and avoids confusion about runtime differences between "record" and "normal" classes (There is no difference, if the programmer correctly implements the properties, is operator, Equals and GetHashCode)
  2. Replace the word "record" with the word "immutable" and have it mean what it says: that the class/struct is immutable. Auto generation of is, Equals etc, makes sense for immutable objects. If you want mutability, you have to forego the auto generation and implement the methods yourself (according to whatever strategy makes sense for your mutable object)
  3. Get rid of "record" all together and drive everything off enhanced primary constructor syntax (as per my previous suggestion)
I think all of these options are beneficial for clarity, but I'm starting to think I like the second one best. Its clear, and it gives clearly defined immutability, which I think is of interest to many people. (The third option is more flexible, but also possibly more complex or confusing)

I see immutability is rumoured to be on the cards: http://wesnerm.blogs.com/net_undocumented/2013/12/immutable-isolated-types-highly-likely-in-future-c.html

I wonder if the draft spec we are discussing here could be split in two: one to deal with pattern matching through (hand-coded) "is" operators, and one to consider records/immutability/generation-of-the-is-operator?
Aug 14, 2014 at 9:10 AM
Why are they called "records"? Is this terminology used by any language other than F#? Perhaps writing "immutable class" would make more sense?
Aug 14, 2014 at 10:42 AM
Edited Aug 14, 2014 at 10:47 AM
johnrusk wrote:
  1. Replace the word "record" with the word "autogenerate". This makes it clear what the keyword does, and avoids confusion about runtime differences between "record" and "normal" classes (There is no difference, if the programmer correctly implements the properties, is operator, Equals and GetHashCode)
Nah. Nobody is confused about the difference in F# either, where you also have records, discriminated unions and regular classes, which, at runtime, all boil down to regular classes. It'll just take some getting used to, if you've never done any functional programming before.
  1. Replace the word "record" with the word "immutable" and have it mean what it says: that the class/struct is immutable. Auto generation of is, Equals etc, makes sense for immutable objects. If you want mutability, you have to forego the auto generation and implement the methods yourself (according to whatever strategy makes sense for your mutable object)
Immutability and auto-generation should not be mixed up. If F# doesn't require it, why should C#? After all, F# is supposed to be the pure functional language, whereas C# just tries to catch up on functional programming features. But first and foremost, C# is imperative and should therefore support the usual imperative features, where mutability is one of them. Sure you can shoot yourself into the foot, but note that the proposal does try to steer you into the pit of success by making immutability the default. If you know what you do, you can safely ignore the default if you need to. If you don't know what you do and run into problems ... well ... it cannot be the goal of a programming language to protect ignorant people from themselves.
  1. Get rid of "record" all together and drive everything off enhanced primary constructor syntax (as per my previous suggestion)
Not sure if that'll work in all cases.
Aug 14, 2014 at 10:55 AM
In F# types are Immutable be default, in C# and vb.net it is the opposite mutable by default. As well defining immutable type (especially reference type) much harder.
Every operation or manipulation results in a new instance of the type being created.

I think String is the only example of an immutable reference type that I know. (Other examples would be good to see and know).
Aug 14, 2014 at 8:13 PM
Edited Aug 14, 2014 at 8:14 PM
AdamSpeight2008 wrote:
I think String is the only example of an immutable reference type that I know. (Other examples would be good to see and know).
What about the Immutable Collections?

For example, it seems the ImmutableDictionary<TKey, TValue> is an immutable reference type.
Aug 14, 2014 at 8:26 PM
dsaf wrote:
Why are they called "records"? Is this terminology used by any language other than F#? Perhaps writing "immutable class" would make more sense?
I believe a number of languages use the term, e.g. Pascal, to mean "a composite data type with no behaviour". I.e. no methods.

@nmgafter: is it expected that c# records will also be "behaviourless"? If so, most of my concerns about the word "record" would evaporate. If not.... well that's why I find the term confusing ;-). To an ex Pascal programmer like me, "record" means "has no methods".
Aug 14, 2014 at 8:29 PM
nmgafter wrote:
@johnrusk All excellent questions. I hope my colleagues who are on the C# language design committee will have an answer.
Do you know whether they are monitoring this discussion thread?
Developer
Aug 14, 2014 at 9:04 PM
johnrusk wrote:
Do you know whether they are monitoring this discussion thread?
I don't expect other members of the language design team to chime in to this thread. But it is a question that I have kept on the table.
Developer
Aug 14, 2014 at 9:06 PM
johnrusk wrote:
dsaf wrote:
Why are they called "records"? Is this terminology used by any language other than F#? Perhaps writing "immutable class" would make more sense?
I believe a number of languages use the term, e.g. Pascal, to mean "a composite data type with no behaviour". I.e. no methods.

@nmgafter: is it expected that c# records will also be "behaviourless"? If so, most of my concerns about the word "record" would evaporate. If not.... well that's why I find the term confusing ;-). To an ex Pascal programmer like me, "record" means "has no methods".
No. But since Pascal doesn't have composite data types with behavior either, your expectation doesn't really translate to an object-oriented language.
Aug 15, 2014 at 6:54 AM
On a completely different note, does pattern matching play nicely with interfaces? E.g. can you do this
if (obi is IFoo(var x, *))...
I'm guessing the answer is no, because where would the "is" operator be defined.
Aug 15, 2014 at 10:22 AM
johnrusk wrote:
On a completely different note, does pattern matching play nicely with interfaces? E.g. can you do this
if (obi is IFoo(var x, *))...
I'm guessing the answer is no, because where would the "is" operator be defined.
Nice question. I'm also guessing not. On the other side it also makes sense following the symmetry. Classes have constructors and deconstructors, but interfaces do not.

You could still used deinitializers if approved:
if(obj is IFoo{ X = var x })
Aug 15, 2014 at 6:04 PM
Edited Aug 15, 2014 at 6:25 PM
Olmo wrote:
if(obj is IFoo{ X = var x })
Looking at this syntax again, something is starting to rub me the wrong way. Like, the syntax itself is going the wrong way. It takes what is on the left side of the =, and assigns it to the right. Does this make anyone else feel slightly uneasy? I'd like to propose an amendment to this proposal to a proposal, replacing = with is.
if(obj is IFoo{ X is var x })
if(obj is IFoo{ X is EvenNumber() })
window.FindControl(c => c is Border{ Child is ComboBox{ Name is "bla",
                                                        Orientation is Orientation.Left}})
Or going a step further, allowing comparison operators.
window.FindControl(c => c is Border{ Child is ComboBox{ Name == "bla",
                                                        Orientation == Orientation.Left}})
if(box is Rectangle{ Left > 0, Width is var x })
Or, ditching the duality proposal completely.
window.FindControl(c => c is Border{ Child is ComboBox{ Name == "bla" &&
                                                        Orientation == Orientation.Left}})
if(box is Rectangle{ Left > 0 && Width is var x })
In which case the contents of the braces is just a normal conditional, within the context of the object being matched. As if there was a with block. This, along with && could eliminate the need for the when clause in case statements.
case EvenNumber() && int i && i > 0:
Would we need a way to reference the context's object itself? OK, I'm getting waaay too ahead of myself. I need to stop and come back later.
Aug 15, 2014 at 6:59 PM
Edited Aug 15, 2014 at 6:59 PM
Why not take utilize the coding pattern for pattern matching from nemerle (a c# style .net programming language) ?
factorial(n, acc)
{
  match (n , acc) with
  { | (0, _)
    | (1, _) => acc;
    | _      => factorial(n - 1, n * acc);
  }
}
Aug 16, 2014 at 2:34 AM
AdamSpeight2008 wrote:
Why not take utilize the coding pattern for pattern matching from nemerle (a c# style .net programming language) ?
I do not think the pipeline character fits well to C#, it was probably adopted by Nemerle from F# or OCaml. I do however like the idea of a "match" keyword and I'm surprised that this hasn't been suggested yet by someone. One of the reasons languages have keywords is to make the language more readable and faster to parse for a human, and for that reason it could make sense to give pattern matching a separate keyword so that it is easily recognized among the usual if's and switches.
Aug 16, 2014 at 3:32 AM
Olmo wrote:
You could still used deinitializers if approved:
if(obj is IFoo{ X = var x })
Nice idea.
Aug 16, 2014 at 12:23 PM
Yota wrote:
Olmo wrote:
if(obj is IFoo{ X = var x })
Looking at this syntax again, something is starting to rub me the wrong way. Like, the syntax itself is going the wrong way. It takes what is on the left side of the =, and assigns it to the right. Does this make anyone else feel slightly uneasy? I'd like to propose an amendment to this proposal to a proposal, replacing = with is.
Then why not just switch sides var x = X? It's in a brace, so is there something ambiguous?

Perhaps I misunderstand your syntax, but I think expecting nothing more than a double equal or an equal to imply assignment or comparison is a bad idea. All the other places we make this distinction has a good bit of context so we are not actually relying on pattern matching a very subtle difference in our heads.
Aug 16, 2014 at 4:26 PM
Edited Aug 17, 2014 at 12:52 PM
About the idea to flip the order of assignment. It's true that for the simple cases (Property = var myVar) flipping could have some advantage but let's see more complex cases:
x is Button{ Name = var name, Id = *, Content = Image { Path = var path } img }
I think this version is clear, but using the new syntax...
x is Button{ var name = Name , * = Id,  Image { var path = Path } img = Content}
I think this one is much harder to parse, the more similar constructing-deconstructing the better
Aug 16, 2014 at 4:55 PM
christoph_hausner wrote:
I do not think the pipeline character fits well to C#, it was probably adopted by Nemerle from F# or OCaml.
The piping operators in nemerle are <| and |> . The meaning of | in a match is more akin to a case statement in a select, but much shorter to type.
When I use a match in nemerle I like to position the | so it aligns with the letter t in match, as a aide-memoire. Does XXX match with THIS
I do however like the idea of a "match" keyword and I'm surprised that this hasn't been suggested yet by someone.
Using match allows it to have a different semantic meaning to switch
match ( op ) with
{ | add             => return x + y; 
  | sub             => return x - y;
  | div when y != 0 => return x / y;
  | mul             => return x * y;
  | pwr             => return x ^ y;
  | _               => thrown new UnknownOp;
}
Using an example proposal
Expr Deriv(Expr e)
{
 switch (e)
 {
   case X(): return Const(1);
   case Const(*): return Const(0);
   case Add(var Left, var Right): return Add(Deriv(Left), Deriv(Right));
   case Mult(var Left, var Right): return Add(Mult(Deriv(Left), Right), Mult(Left, Deriv(Right)));
   case Neg(var Value): return Neg(Deriv(Value));
  }
}

vs
Expr Deriv(Expr e)
{
 match (e) with
 {
   | X()                       => return Const(1);
   | Const(*)                  => return Const(0);
   | Add(var Left, var Right)  => return Add(Deriv(Left), Deriv(Right));
   | Mult(var Left, var Right) => return Add(Mult(Deriv(Left), Right), Mult(Left, Deriv(Right)));
   | Neg(var Value)            => return Neg(Deriv(Value));
  }
}
It could also be used as an expression (like nemerle) rather than a statement block, so can be used in a expression.
var derived = match (e) with
              {
                | X()                       => return Const(1);
                | Const(*)                  => return Const(0);
                | Add(var Left, var Right)  => return Add(Deriv(Left), Deriv(Right));
                | Mult(var Left, var Right) => return Add(Mult(Deriv(Left), Right), Mult(Left, Deriv(Right)));
                | Neg(var Value)            => return Neg(Deriv(Value));
               };
Aug 16, 2014 at 7:47 PM
I thought of something like
match (x)
{
    case X():
    case Add(var left, var right):
    case Neg(var value):
}
or
match (x)
{
    with X():
    with Add(var left, var right):
    with Neg(var value):
}
to keep it more closely to existing C# syntax.
Aug 17, 2014 at 5:05 AM
Edited Aug 17, 2014 at 1:53 PM
Olmo wrote:
About the idea to flip the order of assignment. It's true that for the simple cases (Property = var myVar) flipping could have some advantage but let's see more complex cases:
x is Button{ Name = var name, Id = *, Content = Image { Path = var path } img }
I find this much harder to parse than other way around:
x is Button{ var name = Name , * = Id,  Image { var path = Path } img = Content}
I'd like to say that i share Yota and Kathleen's sentiments that the proposed syntax reverses the direction of the assignment operator and i would also say conflates the assignment and equality operations.

Olmo I'm not sure i understand your example. The first assignment is obvious, but the second i think highlights the clumsiness of the '*' wildcard. Why is it needed. In F# '_' is needed since so most of the typing is inferred, but here we specify the type 'Button' so given that can't we just omit the parameters that we don't care about?

Beyond this, the 3rd assignment is very confusing. Shortly put, what type is 'Button.Content'?

Is it Object or some superclass of 'Image'? If so then i would interpret the operation as trying to match the type to Image, in which case i would say it needs its own 'is' keyword to make sense.

Is 'Content' exactly type 'Image' or some subtype of 'Image'? If so then i would interpret this assigning the 'Path' property of 'Image' to the new 'path' variable. But i would propose this could be more sensibly put (with Kathleen's suggestion in mind and omitting the unneeded wildcard/blackhole assignment) as :

x is Button{ var name = Name , var path = Content.Path }

Furthermore i'm not sure what 'img' is trying to indicate, if it is a variable declaration/assignment then i would argue is needs the 'var' keyword or a fixed type designation which in totality would look like:

x is Button{ var name = Name , var path = Content.Path, var img = Content }

Olmo, I might be barking up the wrong tree here so set me straight if I've misunderstood.


As to AdamSpeight2008 and christoph_hauser, why is the switch keyword inadequate? I think Olmo's suggested syntax extensions (with Kathleen's suggested modification) are extraordinarily useful and very C#-ish. This is of course entirely subjective, but why add a new keyword. But i do really like the implicit naming you show examples of since it's similar to object creation syntax used in anonymous types. The above statement could just be:

x is Button{ var Name, var Content.Path, var img = Content }

Where 'var Name' is a shortened form of 'var Name = Button.Name' (i know 'Button' isn't in scope but i put it for clarity). is pretty nice.

As to having the switch become an expression, i would love that but nmgafter stated earlier :
Switch as an expression is fairly orthogonal to most of this record/pattern stuff, though clearly there is significant synergy. I don't want to fold that into the current specification for fear of complicating the initial internal discussion about these features.
Lastly i would like to thank everyone contributing here for forming out these great ideas. It makes me very excited for the future of the language.

Edit : I mistakenly attributed this idea entirely to Kathleen instead of Yota. Also Typos
Aug 17, 2014 at 5:37 AM
Olmo wrote:
About the idea to flip the order of assignment. It's true that for the simple cases (Property = var myVar) flipping could have some advantage but let's see more complex cases:
x is Button{ Name = var name, Id = *, Content = Image { Path = var path } img }
I find this much harder to parse than other way around:
x is Button{ var name = Name , * = Id,  Image { var path = Path } img = Content}
Yeah, I thought about it, but didn't bring it up for the same reason. It's too unreadable. This is what led me to the thought of replacing = with is. Looking at it, it just seemed more consistent.
if (Left is int i) ...
if (box is Rectangle{ Left is int i }) ...
 // vs
if (Left is int i) ...
if (box is Rectangle{ Left = int i }) ...
My last post did start to zoom off the tracks at some point, however. I believe enabling comparison operators, though useful, could come along with come sneaky consequences, such as subtle behavior changes when dealing with dynamic vars. Perhaps a syntax along the lines of x is > 0 could overcome those consequences. I feel like we've already got a ton of new syntax though, so I may just keep that at the back of my mind. Worst case scenario, we can just do x is GreaterThan(0).
Aug 18, 2014 at 12:47 AM
Yota wrote:
if (Left is int i) ...
if (box is Rectangle{ Left is int i }) ...
 // vs
if (Left is int i) ...
if (box is Rectangle{ Left = int i }) ...
While this does avoid the inversion of the '=' operator, it increases the load on the 'is' operator even further doesn't it?
Aug 18, 2014 at 8:13 AM
Edited Aug 20, 2014 at 9:13 AM
Just a few questions:

Section 1.4: Is Expression

The BNF
  relational-expression:
    relational-expression is complex-pattern
    relational-expression is type
is recursive in all branches. What does it mean?

Section 1.5.1 Type Pattern

In
  complex-pattern:
    type identifier
could one make the identifier optional? Or allow the identifier to be replaced by the wildcard-pattern ?

Section 1.6 Switch Statement

"In some cases the compiler can prove that a switch section can have no effect at runtime because its pattern is subsumed by a previous pattern. In these cases a warning may be produced."

Just an idea: One could have a statement named e.g. "impossible" which indicates that the compiler is expected to be able to prove that a given branch cannot be reached. That would allow to write e.g. "default : impossible" at the end of a switch. Downside: To ensure program portability this would require a precise specification of how much the compiler can prove.

Section 1.6 Switch Statement

"??? If a match section contains more than one match label, then it is a compile-time error for any variables to be defined in the patterns (for example by the use of a var pattern, type pattern, etc)."

Looks a bit restrictive. Maybe one would like to write
  case RedRectangle(width, height):
  case GreenRectangle(width, height):
  case BlueRectanble(width, height):
    ... code using width and height
That should be easy if all patterns define the same variables and each defined variable has the same type in all patterns.

By the way, continuing the example in Section 1.8.4, is the following presently legal (there are no defined variables):
int Arity(Expr e)
{
  switch(e) {
    case X():
    case Const(*):
      return(0);
    case Neg(*):
      return(1);
    case Add(*,*):
    case Mult(*,*):
      return(2);
  }
}
An example of use of having several patterns which define the same variables could read
int Size(Expr e)
{
  switch(e) {
    case X():
    case Const(*):
      return 1;
    case Neg(e1):
      return 1 + Size(e1);
    case Add(e1, e2):
    case Mult(e1, e2):
      return 1 + Size(e1) + Size(e2);
  }
}
Aug 18, 2014 at 9:49 AM
Edited Aug 20, 2014 at 9:13 AM
Deleted
Aug 21, 2014 at 2:36 AM
kgrue wrote:

Section 1.6 Switch Statement

"In some cases the compiler can prove that a switch section can have no effect at runtime because its pattern is subsumed by a previous pattern. In these cases a warning may be produced."

Just an idea: One could have a statement named e.g. "impossible" which indicates that the compiler is expected to be able to prove that a given branch cannot be reached. That would allow to write e.g. "default : impossible" at the end of a switch. Downside: To ensure program portability this would require a precise specification of how much the compiler can prove.
I don't understand what you are proposing here.

Section 1.6 Switch Statement

"??? If a match section contains more than one match label, then it is a compile-time error for any variables to be defined in the patterns (for example by the use of a var pattern, type pattern, etc)."

Looks a bit restrictive. Maybe one would like to write
  case RedRectangle(width, height):
  case GreenRectangle(width, height):
  case BlueRectanble(width, height):
    ... code using width and height
That should be easy if all patterns define the same variables and each defined variable has the same type in all patterns.
Totally agree. If all patterns introduce all and the same variables (i.e. same name and same type), it should be valid.
Aug 21, 2014 at 8:07 AM
Edited Aug 21, 2014 at 11:39 AM
PauloMorgado wrote:
kgrue wrote:

Section 1.6 Switch Statement

"In some cases the compiler can prove that a switch section can have no effect at runtime because its pattern is subsumed by a previous pattern. In these cases a warning may be produced."

Just an idea: One could have a statement named e.g. "impossible" which indicates that the compiler is expected to be able to prove that a given branch cannot be reached. That would allow to write e.g. "default : impossible" at the end of a switch. Downside: To ensure program portability this would require a precise specification of how much the compiler can prove.
I don't understand what you are proposing here.
My proposal, which is really just a (possibly bad) idea is related to the Draft Spec, Section 1.9 Open Issues, Last Bullet: Should there be a way to declare that a switch-statement is intended to be complete? My proposal is to allow to write e.g. this:
int Size(Expr e)
{
  switch(e) {
    case X():
    case Const(*):
      return 1;
    case Neg(e1):
      return 1 + Size(e1);
    case Add(e1, e2):
    case Mult(e1, e2):
      return 1 + Size(e1) + Size(e2);
    default:
      impossible;
  }
}
The intended behavior of the compiler is:

If the compiler can prove that the default case is unreachable then the compiler generates no code for the default case and issues no warning.

Otherwise the compiler issues a warning and generates a default case which throws an exception.

The intended behavior of a coverage tool is that the tool reports 100% coverage if all but the default branch is covered by test cases. In other words, a coverage tool is intended to disregard branches which are marked as unreachable.

Now another example:
int Size(Expr e)
{
  SOME CODE
  switch(e) {
    case X():
      impossible;
    case Neg(e1):
      return 1 + Size(e1);
    case Add(e1, e2):
    case Mult(e1, e2):
      return 1 + Size(e1) + Size(e2);
    default:
      return 1;
  }
}
In this example, the compiler is asked to verify that the e in switch(e) cannot be an X().

My proposal is to allow each switch statement to contain any number (zero or more) instances of the "impossible" statement.
Aug 21, 2014 at 3:59 PM
Edited Aug 21, 2014 at 4:01 PM
kgrue wrote:
The intended behavior of the compiler is:

If the compiler can prove that the default case is unreachable then the compiler generates no code for the default case and issues no warning.

Otherwise the compiler issues a warning and generates a default case which throws an exception.

The intended behavior of a coverage tool is that the tool reports 100% coverage if all but the default branch is covered by test cases. In other words, a coverage tool is intended to disregard branches which are marked as unreachable.
This seems like a rather large request to put on the compiler itself. You typically need to actually run the code to see code coverage. (unit tests or pex)
switch(someinteger) {
  case IsEven(): ...
  case IsOdd(): ...
  default: impossible;
}
I don't think even D's value range propagation would be able to validate that this default branch is unreachable.

Unless I'm missing something, the only thing the compiler could reasonably look at is the types. In your first example, what would happen if a new class deriving from Expr were later introduced in another assembly or version?
Aug 21, 2014 at 5:57 PM
Yota wrote:
This seems like a rather large request to put on the compiler itself. You typically need to actually run the code to see code coverage. (unit tests or pex)
The problem is known to be unsolvable in its entirety (since it's as hard as halting problem), so we can expect from a compiler to determine unreachability only in a small number of obvious cases.
Aug 22, 2014 at 8:03 AM
Edited Aug 22, 2014 at 9:14 AM
Yota wrote:
kgrue wrote:
The intended behavior of the compiler is:

If the compiler can prove that the default case is unreachable then the compiler generates no code for the default case and issues no warning.

Otherwise the compiler issues a warning and generates a default case which throws an exception.

The intended behavior of a coverage tool is that the tool reports 100% coverage if all but the default branch is covered by test cases. In other words, a coverage tool is intended to disregard branches which are marked as unreachable.
This seems like a rather large request to put on the compiler itself. You typically need to actually run the code to see code coverage. (unit tests or pex)
I agree. It was my intension that compilation was handled by the compiler and coverage was handled by a coverage tool (e.g. nunit + dotcover) and that coverage was measured by running a test suite. Maybe you misread what I wrote because of the line breaks, so here is another try:
The intended behavior of the compiler is: If the compiler can prove that the default case is unreachable then the compiler generates no code for the default case and issues no warning. Otherwise the compiler issues a warning and generates a default case which throws an exception.

The intended behavior of a coverage tool is that the tool reports 100% coverage if all but the default branch is covered by test cases. In other words, a coverage tool is intended to disregard branches which are marked as unreachable.
I will answer your other point in a separate reply to keep issues separate.
Aug 22, 2014 at 8:43 AM
Yota wrote:
switch(someinteger) {
  case IsEven(): ...
  case IsOdd(): ...
  default: impossible;
}
I don't think even D's value range propagation would be able to validate that this default branch is unreachable.
I'm not sure I understand what case IsEven() means. But I can comment on this example:
switch(someUnsignedInteger % 2) {
  case 0: ...
  case 1: ...
  default: impossible;
}
I would expect that some (most?) C# compilers would give up on this one, issue a warning, and generate a default case which throws an exception. I would expect some (if any) C# compilers would succeed to prove that the default case is unreachable. There exist aggresive optimizing compilers for other languages than C# which would have no problems proving the unreachability of the default case.

Yota wrote:
Unless I'm missing something, the only thing the compiler could reasonably look at is the types.
Agreed. Except that the compiler can also look at constants:
void sillyFunc(int x)
{
  if (x == 0) return;
  switch(x) {
    case 0: impossible;
    default: return;
  }
}
I suppose it is ok that the compiler only looks at types and constants since pattern matching is mainly concerned with looking at types and constants.

Yota wrote:
In your first example, what would happen if a new class deriving from Expr were later introduced in another assembly or version?
The spec, Section 1.7: "Some possible optimizations" says:
It would be possible to support declaring a type hierarchy closed, meaning that all subtypes of the given type are declared in the same assembly.
My example (and the entire proposal of introducing an impossible statement) only makes sense if it is possible to declare Expr closed.
Aug 22, 2014 at 1:24 PM
Edited Aug 22, 2014 at 1:37 PM
VladD wrote:
Yota wrote:
This seems like a rather large request to put on the compiler itself. You typically need to actually run the code to see code coverage. (unit tests or pex)
The problem is known to be unsolvable in its entirety (since it's as hard as halting problem), so we can expect from a compiler to determine unreachability only in a small number of obvious cases.
I do not know whether or not my answers to Yota also answered your concerns. In any case: You are of course right that reachability is undecidable and that Turings result on the halting problem can be used for proving the undecidability. Nevertheless, many compilers do reachability analysis and issue a warning if they can prove that some code is unreachable. The impossible statement just inverts that behavior so that the compiler issues a warning if it cannot prove some particular code to be unreachable. One use of impossible would be the ability to write default: impossible like in the example below where the compiler is asked to verify that all kinds of Expr are covered. That is of course an "obvious case" using your words but nevertheless a useful one.
int Size(Expr e)
{
  switch(e) {
    case X():
    case Const(*):
      return 1;
    case Neg(e1):
      return 1 + Size(e1);
    case Add(e1, e2):
    case Mult(e1, e2):
      return 1 + Size(e1) + Size(e2);
    default:
      impossible;
  }
}
Developer
Aug 22, 2014 at 8:40 PM
We have pushed a first (rather raw) prototype of compiler code to implement this feature onto the branch "pattern-matching" of the Roslyn repository, which you can see here:

https://roslyn.codeplex.com/SourceControl/changeset/00552fc2287f820ae9d42fd259aa6c07c2c5a805

This includes support for pattern-matching based on existing readable properties, using a syntax like
if (box is Rectangle{ Left is int i }) ...
We don't yet have an easy way for you to try these changes in VS.
Aug 22, 2014 at 8:42 PM
kgrue wrote:
I do not know whether or not my answers to Yota also answered your concerns. In any case: You are of course right that reachability is undecidable and that Turings result on the halting problem can be used for proving the undecidability. Nevertheless, many compilers do reachability analysis and issue a warning if they can prove that some code is unreachable. The impossible statement just inverts that behavior so that the compiler issues a warning if it cannot prove some particular code to be unreachable. One use of impossible would be the ability to write default: impossible like in the example below where the compiler is asked to verify that all kinds of Expr are covered. That is of course an "obvious case" using your words but nevertheless a useful one.
The problem with this approach is following: spurious warnings! If a compiler can prove something is unreachable, the warning is fully justified; absence of warning is okay. But if you rely on compiler's ability to prove unreachability, you may have a false positive warning. Given that a lot of serious project use option "Treat warnings as errors", spurious warnings are not a good thing.
Aug 22, 2014 at 10:15 PM
nmgafter wrote:
We have pushed a first (rather raw) prototype of compiler code to implement this feature onto the branch "pattern-matching" of the Roslyn repository, which you can see here:

https://roslyn.codeplex.com/SourceControl/changeset/00552fc2287f820ae9d42fd259aa6c07c2c5a805

This includes support for pattern-matching based on existing readable properties, using a syntax like
if (box is Rectangle{ Left is int i }) ...
We don't yet have an easy way for you to try these changes in VS.
Nice. Hopefully you're able to streamline the process for testing with VS.

Are records implemented yet or just pattern matching? Is the spec still up-to-date with your latest thinking on the feature? (I know it's changed a lot based on feedback from this thread)
Developer
Aug 22, 2014 at 10:28 PM
MgSam wrote:
Nice. Hopefully you're able to streamline the process for testing with VS.
I'm not sure what resources the compiler team will devote to using branches/forks with VS. I'll post here if I become aware of a way.
Are records implemented yet or just pattern matching? Is the spec still up-to-date with your latest thinking on the feature? (I know it's changed a lot based on feedback from this thread)
Yes, records are implemented. The spec doesn't include a description of support for readable properties, even though it is implemented. Otherwise it is about up to date. We have ideas for other things (e.g. support for tuples) that are neither in the spec nor the prototype.
Aug 23, 2014 at 12:50 AM
nmgafter wrote:
We have ideas for other things (e.g. support for tuples) that are neither in the spec nor the prototype.
<offtopic>Support for tuples? Is it something like this discussion? Could you please give us some insider information on that?</offtopic>
Aug 24, 2014 at 8:23 AM
Edited Aug 24, 2014 at 8:46 AM
nmgafter wrote:
This includes support for pattern-matching based on existing readable properties, using a syntax like
if (box is Rectangle{ Left is int i }) ...
Excellent! Will the equal sign be also supported? Having to repeat the property type is odd...
 if (box is Rectangle{ Left = var i }) ...
Also how you match with constant properties with the is syntax?
 if (box is Rectangle { Left is 10, Right is int r }) ...
That also looks strange.

I think the point of the equal sign looking like an assignment is not really an issue. Pattern matching with the is operator also looks like a method call with input parameters for constants for example
Aug 24, 2014 at 3:38 PM
Olmo wrote:
Also how you match with constant properties with the is syntax?
 if (box is Rectangle { Left is 10, Right is int r }) ...
That also looks strange.

I think the point of the equal sign looking like an assignment is not really an issue. Pattern matching with the is operator also looks like a method call with input parameters for constants for example
How about assignment and equality operators, but with the inverted syntax as my earlier post touched on.
 if (box is Rectangle { Left == 10, int r = Right }) ...
Aug 24, 2014 at 10:09 PM
Since this is a feature that I wish C# had almost daily, I decided to pull the branch and compile it and run through some sample programs.

First off was the canonical expression parser. As is seen in this code https://gist.github.com/Mr-Byte/08355f69dde639aa3ea1

This didn't work exactly how I would have expected it to work, as the left and right variables are null in every case, but the Add case. I'm guessing this is due to switch scoping rules because giving each of the variables in each case a distinct name will not cause an exception to be thrown. This seems like an area that can potentially cause a lot of confusion and definitely needs to be looked at if pattern matching moves forward using switch statements for large matches.

Next up are record classes that specify no primary constructor such as https://gist.github.com/Mr-Byte/0a3b36118cb3a3dce13e

The None case really needs no primary constructor, because None generally does not store any data (an empty class, essentially); however that means None generates no is operator override. So the code as shown here will not compile. There's two ways to fix it. One is to specify an empty primary constructor and the other is to remove the () after None in the if statement. While not really a big issue, it feels inconsistent that a primary constructor (even if parameter-less) must be specified to get an is operator to be generate on a record class.

While talking about option types, the following does not compile either https://gist.github.com/Mr-Byte/40e8abb593451ccce2ec

It would be nice if the compiler could infer the class' generic parameter based on the type information provided. I'm not sure if it is possible, but it would be extremely useful in reducing the verbosity of pattern matching when working with record types that are also generic.

If you do specify the generic type, the code compiles and runs just fine.

And while I understand this is a prototype to help flesh out the idea, the following code will completely crash the compiler https://gist.github.com/Mr-Byte/29f556db56aa8c72481e

A record class with no primary constructor and any method specified (in this case ToString, but it crashes with others) seems to be the cause. The method can be either a regular method or an expression-bodied member and the crash will happen. Specifying a primary constructor stops the crash from occurring.

Besides the few issues I've ran into, I'm certainly liking the direction this is taking. It would be nice to be able to "seal" a hierarchy of record classes, similar to how Scala works with sealed abstract base classes when using case classes. This would allow the compiler to know that a type hierarchy can't be further extended and that no default case is required in the switch statement.
Developer
Aug 25, 2014 at 2:50 AM
ByteMR wrote:
First off was the canonical expression parser. As is seen in this code https://gist.github.com/Mr-Byte/08355f69dde639aa3ea1

This didn't work exactly how I would have expected it to work, as the left and right variables are null in every case, but the Add case. I'm guessing this is due to switch scoping rules because giving each of the variables in each case a distinct name will not cause an exception to be thrown.
This is "just" a bug. We didn't yet implement the scoping rules we intend (identifiers introduced in a switch case are in scope only within that switch block, and are definitely assigned only if there is a single switch label), and we also didn't provide diagnostics when there are name conflicts due to conflicts because we don't implement the scope rules correctly. This definitely needs to be fixed.
Next up are record classes that specify no primary constructor such as https://gist.github.com/Mr-Byte/0a3b36118cb3a3dce13e
We didn't intend to allow that, but we should treat no argument list the same as an empty argument list. Although it isn't supported, failing to specify an argument list isn't diagnosed, and can cause a crash, as you observed. Bugs.
It would be nice if the compiler could infer the class' generic parameter based on the type information provided. I'm not sure if it is possible, but it would be extremely useful in reducing the verbosity of pattern matching when working with record types that are also generic.
I'm going to suspend judgment on this for now.
Besides the few issues I've ran into, I'm certainly liking the direction this is taking. It would be nice to be able to "seal" a hierarchy of record classes, similar to how Scala works with sealed abstract base classes when using case classes. This would allow the compiler to know that a type hierarchy can't be further extended and that no default case is required in the switch statement.
Agreed. I think that makes sense but I don't know the best way to express it.
Aug 25, 2014 at 9:23 AM
Edited Aug 25, 2014 at 9:27 AM
nmgafter wrote:
This is "just" a bug. We didn't yet implement the scoping rules we intend (identifiers introduced in a switch case are in scope only within that switch block, and are definitely assigned only if there is a single switch label), and we also didn't provide diagnostics when there are name conflicts due to conflicts because we don't implement the scope rules correctly. This definitely needs to be fixed.
Wait -- What? Are you saying that ByteMR's code should not compile at all?
 switch(expression)
{
case Val(decimal value): return value;
case Add(Expr left, Expr right): return Evaluate(left) + Evaluate(right);
case Sub(Expr left, Expr right): return Evaluate(left) - Evaluate(right);
case Mul(Expr left, Expr right): return Evaluate(left) * Evaluate(right);
case Div(Expr left, Expr right): return Evaluate(left) / Evaluate(right);
default: throw new ArgumentOutOfRangeException("expression");
}
Are you saying there should be a compiler error complaining that left and right are declared multiple times within the same scope? That's not what I would expect to happen at all and would greatly reduce the ease-of-use of pattern matching. After all, you'd have to come up with different names for all those variables (probably left1 to left4, right1 to right4) for no obvious benefit: You'd probably never access left1 in the Div(left4, right4) case. F# also allows reusing variable names of previous pattern match cases.

But then again I've always found the scoping rules of switch statements extremely strange. For me, a case should introduce a new scope, therefore names of locally declared variables should not leak out to other cases. In normal switch statements, you can at least put the body of the case into its own block (even though that's visually unpleasant for my eyes). You can't do that for pattern cases because the variables are declared in the case declaration itself.

Anyway, I know you can't change the scoping rules for switch statements, but you could use different ones for pattern switches. If you don't want to do that, I strongly suggest introduces a new match keyword that doesn't have the scoping rules of switch statements that are - in my opinion - not suitable when pattern matching.
Aug 25, 2014 at 11:45 AM
nmgafter wrote:
This is "just" a bug. We didn't yet implement the scoping rules we intend (identifiers introduced in a switch case are in scope only within that switch block, and are definitely assigned only if there is a single switch label), and we also didn't provide diagnostics when there are name conflicts due to conflicts because we don't implement the scope rules correctly. This definitely needs to be fixed.
Please, don't do that. If all matching variables are used in all switch cases, then it should be valid.
Aug 25, 2014 at 11:49 AM
Expandable wrote:
But then again I've always found the scoping rules of switch statements extremely strange. For me, a case should introduce a new scope, therefore names of locally declared variables should not leak out to other cases. In normal switch statements, you can at least put the body of the case into its own block (even though that's visually unpleasant for my eyes). You can't do that for pattern cases because the variables are declared in the case declaration itself.
What scoping rules of switch in particular do you find extremely strange?

Expandable wrote:
Anyway, I know you can't change the scoping rules for switch statements, but you could use different ones for pattern switches. If you don't want to do that, I strongly suggest introduces a new match keyword that doesn't have the scoping rules of switch statements that are - in my opinion - not suitable when pattern matching.
I don't think there's a need for that.
Aug 25, 2014 at 11:59 AM
Edited Aug 25, 2014 at 12:23 PM
PauloMorgado wrote:
What scoping rules of switch in particular do you find extremely strange?
switch (someInt)
{
    case 0:
       var x = 1; // compiler error, conflicting x defined below
       break;
    case 1:
       var x = 2; // compiler error, x already defined above
       break;
}
Therefore, the pattern match example above would raise the same error, if I understand nmgafter correctly:
switch(expression)
{
   case Val(decimal value): return value;
   case Add(Expr left, Expr right): return Evaluate(left) + Evaluate(right); 
              // compiler error, conflicting left/right defined below
   case Sub(Expr left, Expr right): return Evaluate(left) - Evaluate(right); 
              // compiler error, left/right already defined above
   case Mul(Expr left, Expr right): return Evaluate(left) * Evaluate(right); 
              // compiler error, left/right already defined above
   case Div(Expr left, Expr right): return Evaluate(left) / Evaluate(right); 
              // compiler error, left/right already defined above
   default: throw new ArgumentOutOfRangeException("expression");
}
In my opinion, both the switch statement above and the pattern match should be allowed. We (probably?) can't fix switch now, but we can avoid the same problem for pattern match switches.

Edit: I know that I could use braces to make the switch above work. But you can't do that with pattern switches, as you can't place a pair of braces around the case clause. I just don't understand why braces are required in the first place. The cases already define the structure of the code, so why do they not introduce a new scope? Is that C heritage? Plus you can't even use the (values of the) variables in another scope, as they might not be definitely assigned. There are other examples of implicit non-braced scopes as well (consider the if (var x = 5) return x; or query-style Linq), so why not allow that for pattern switches as well?

Edit 2: A quick Google search shows that the scoping behavior (or lack thereof) of cases within switch blocks seems to be the source of much confusion, or at least it seems surprising to many people. So I don't think the same decision should be made for pattern match switches, where this problem becomes even more visible. See also the blog post about this issue by Eric Lippert.
Aug 25, 2014 at 2:23 PM
Agree that the current scoping rules around switch present a very annoying problem with pattern matching.

I think most people agree that the current scoping rules are broken. The fact that something like this works is pretty bizarre when people first see it:
var a = "";
switch (a)
{
    case "foo":
        var b = 5;
        break;
    case "bar":
        b = 7;
        break;
}
This already causes lots of confusion for newcomers to C#, and is definitely counter-intuitive.

I'd be in favor of making a breaking change to fix the scoping rules and make each case into its own scope. I think as far as breaking changes go, it wouldn't be that bad, as cases like the example I showed above would become compiler errors (and thus easy to spot and fix). I can't think of an example where currently working code might silently break (which could have happened with the foreach variable lifetime breaking change the team made in C# 4.0) Thus, I think this change would make the language more intuitive, make pattern matching work the way most people expect, resolve confusion, and cause minimal upgrade pain.

In lieu of fixing switch, I'd rather see a construct named something different entirely that has the correct scoping rules. I think settling on the current broken rules for pattern matching is the worst of the 3 outcomes.
Aug 25, 2014 at 2:36 PM
I think the important part of the specification related to this is:
3.7 Scopes
...
  • The scope of a local variable declared in a switch-block of a switch statement (§8.7.2) is the switch-block.
...
Expandable wrote:
switch (someInt)
{
    case 0:
       var x = 1; // compiler error, conflicting x defined below
       break;
    case 1:
       var x = 2; // compiler error, x already defined above
       break;
}
That's not a compiler error. That's a compilation error due to a specification error (my classification). :)

Expandable wrote:
switch(expression)
{
   case Val(decimal value): return value;
   case Add(Expr left, Expr right): return Evaluate(left) + Evaluate(right); 
              // compiler error, conflicting left/right defined below
   case Sub(Expr left, Expr right): return Evaluate(left) - Evaluate(right); 
              // compiler error, left/right already defined above
   case Mul(Expr left, Expr right): return Evaluate(left) * Evaluate(right); 
              // compiler error, left/right already defined above
   case Div(Expr left, Expr right): return Evaluate(left) / Evaluate(right); 
              // compiler error, left/right already defined above
   default: throw new ArgumentOutOfRangeException("expression");
}
In my opinion, both the switch statement above and the pattern match should be allowed. We (probably?) can't fix switch now, but we can avoid the same problem for pattern match switches.

Edit: I know that I could use braces to make the switch above work. But you can't do that with pattern switches, as you can't place a pair of braces around the case clause. I just don't understand why braces are required in the first place. The cases already define the structure of the code, so why do they not introduce a new scope? Is that C heritage? Plus you can't even use the (values of the) variables in another scope, as they might not be definitely assigned. There are other examples of implicit non-braced scopes as well (consider the if (var x = 5) return x; or query-style Linq), so why not allow that for pattern switches as well?

Edit 2: A quick Google search shows that the scoping behavior (or lack thereof) of cases within switch blocks seems to be the source of much confusion, or at least it seems surprising to many people. So I don't think the same decision should be made for pattern match switches, where this problem becomes even more visible. See also the blog post about this issue by Eric Lippert.
Although being a breaking change, like you, I wouldn't expect any side effects in previously written code if this were to be changed. In fact, it would make it easier to implement a per-case block scope.
Developer
Aug 25, 2014 at 3:58 PM
Please read the last sentence of section 1.6 of the draft specification:
The scope of a variable declared in a pattern of a switch-label is the containing switch-section.
That just isn't implemented yet. It isn't a breaking change because existing switch statements never have variables declared in the case label.

ByteMR's code will compile just fine once we've implemented the proper scoping for a variable declared in a pattern of a switch-label.

We're not going to change scoping for local variables declared in the statements of a switch block.
Aug 25, 2014 at 4:59 PM
Edited Aug 25, 2014 at 5:06 PM
nmgafter wrote:
ByteMR wrote:
First off was the canonical expression parser. As is seen in this code https://gist.github.com/Mr-Byte/08355f69dde639aa3ea1

This didn't work exactly how I would have expected it to work, as the left and right variables are null in every case, but the Add case. I'm guessing this is due to switch scoping rules because giving each of the variables in each case a distinct name will not cause an exception to be thrown.
This is "just" a bug. We didn't yet implement the scoping rules we intend (identifiers introduced in a switch case are in scope only within that switch block, and are definitely assigned only if there is a single switch label), and we also didn't provide diagnostics when there are name conflicts due to conflicts because we don't implement the scope rules correctly. This definitely needs to be fixed.
Alright, I wasn't sure if the scoping was intentional or not. I figured intentional because then it would align with the way cases currently work, but it appears that my sample code will work in the future. Keep in mind, right now it compiles. You just get nulls when trying to access the variables in any other case, besides Add. I had read the spec, but I must've glossed over section 1.6 while reading or simply forgotten it. Glad to see my assumptions are wrong and that this will be fixed.

nmgafter wrote:
ByteMR wrote:
Next up are record classes that specify no primary constructor such as https://gist.github.com/Mr-Byte/0a3b36118cb3a3dce13e
We didn't intend to allow that, but we should treat no argument list the same as an empty argument list. Although it isn't supported, failing to specify an argument list isn't diagnosed, and can cause a crash, as you observed. Bugs.
Yeah, I figured it was a bug. Was just going off my own expectations based on my experience with other languages (Scala and F# coming to mind) and found it surprising. Wasn't totally sure if it was a known issue. I understand that this is just a prototype and definitely not the final product.

nmgafter wrote:
ByteMR wrote:
It would be nice if the compiler could infer the class' generic parameter based on the type information provided. I'm not sure if it is possible, but it would be extremely useful in reducing the verbosity of pattern matching when working with record types that are also generic.
I'm going to suspend judgment on this for now.
Understandable. I'm a huge fan of type inference, so that probably skews my opinion. It's not a huge deal if there's no type inference, but it would be helpful in my opinion.

nmgafter wrote:
ByteMR wrote:
Besides the few issues I've ran into, I'm certainly liking the direction this is taking. It would be nice to be able to "seal" a hierarchy of record classes, similar to how Scala works with sealed abstract base classes when using case classes. This would allow the compiler to know that a type hierarchy can't be further extended and that no default case is required in the switch statement.
Agreed. I think that makes sense but I don't know the best way to express it.
I've been thinking of potential ways to express this. I'm not entirely a fan of how Scala does it because that requires all the variants be in the same source file and it really isn't too clear, "sealed abstract" looks like an oxymoron to me in terms of language.

Something that could potentially be expanded upon is perhaps something like:
record namespace Expression
{
    public abstract class Expr {}
    public record class Add(Expr left, Expr right) : Expr;
    public record class Sub(Expr left, Expr right) : Expr;
    public record class Mul(Expr left, Expr right) : Expr;
    public record class Div(Expr left, Expr right) : Expr;
}
Where record namespace would essentially be a namespace for record types. Any classes defined inside this namespace can extend other classes in the namespace within the same assembly, but are then sealed to extension outside the namespace.

This allows you to split the definitions across multiple source files for the same assembly, but ensure that once they're compiled the hierarchy is sealed and the compiler can allow the default case to be ommitted.

Another option, which would most likely not allow methods and properties to be included would be something like:
public record enum Expr
{
    Add(Expr left, Expr right),
    Sub(Expr left, Expr right),
    Mul(Expr left, Expr right),
    Div(Expr left, Expr right)
}
This would ensure that the type being expressed is explicitly sealed and work much like current enums do. You'd match like:
if(expression is Expr.Add(var left, var right))
{
    // Add here
}
I think that might be a nice way of expressing simpler types for pattern matching.

Anyway, I really appreciate your hard work on this feature the openness that's being shown to develop this feature. I really like pattern matching in other languages and I feel like it could really make C# a much more expressive language if it were included. I decided to reserve my input for when the prototype was released so I could get a better feel for the way the proposed feature would work. Now that the prototype is out I'll probably be a lot more active in sharing my input. I like the direction this feature is going so far. I know there's rough edges, but it is a feature that's still early in development which is why I'm very happy you're soliciting feedback on this draft proposal.
Aug 25, 2014 at 5:08 PM
Edited Aug 25, 2014 at 5:11 PM
nmgafter wrote:
Please read the last sentence of section 1.6 of the draft specification:
The scope of a variable declared in a pattern of a switch-label is the containing switch-section.
That just isn't implemented yet. It isn't a breaking change because existing switch statements never have variables declared in the case label.

ByteMR's code will compile just fine once we've implemented the proper scoping for a variable declared in a pattern of a switch-label.

We're not going to change scoping for local variables declared in the statements of a switch block.
Thanks for the clarification.

Even so, it might be worth having the language design committee consider changing the scoping rules for locals in switch if the dev cost is not too significant. As it stands, it will feel very inconsistent (and no doubt be confusing for many) when variables declared via the as operator have a different scope than locals. You're making some significant tinkers to switch anyway- why not fix it's biggest problem?
switch(expression)
{
   case Mul(Expr left, Expr right): 
        var result = Evaluate(left) * Evaluate(right); 
        return result;
   case Div(Expr left, Expr right): 
        var result = Evaluate(left) / Evaluate(right);  //ERROR?
        return result;
}
I can already foresee all the StackOverflow questions asking about why this is so inconsistent.
Developer
Aug 25, 2014 at 5:54 PM
Edited Aug 25, 2014 at 5:56 PM
MgSam wrote:
Even so, it might be worth having the language design committee consider changing the scoping rules for locals in switch if the dev cost is not too significant.
I will not suggest any language changes that would break existing code.

Anyway, the biggest problem with switch is that one case block "falls through" into another. The only reason for that was consistency with C. I don't see changing that either.
Aug 25, 2014 at 6:03 PM
nmgafter wrote:
MgSam wrote:
Even so, it might be worth having the language design committee consider changing the scoping rules for locals in switch if the dev cost is not too significant.
I will not suggest any language changes that would break existing code.

Anyway, the biggest problem with switch is that one case block "falls through" into another. The only reason for that was consistency with C. I don't see changing that either.
As long as the variables from a match case bind to the switch-section instead of the switch-block, I think it'll be fine. Declaring an explicit block such as:
switch(expression)
{
    case Mul(Expr left, Expr right): 
    {
        var result = Evaluate(left) * Evaluate(right); 
        return result;
    }
    case Div(Expr left, Expr right): 
    {
        var result = Evaluate(left) / Evaluate(right);
        return result;
    }
}
Fixes the issue with scoping local variables. I'm also of the personal opinion that it just looks better to do it that way for complex switch cases.

If an alternative match expression is added, it should definitely following scoping locals to the match section, instead of the entire expression.
Aug 25, 2014 at 6:13 PM
@nmgafter Fair enough. Obviously you want to give your proposal the best chance of being adopted by the full design committee.

@ByteMR This makes me think- maybe a good way to "fix" the problem without language changes is to build a diagnostic using Roslyn that requires surrounding case statements with braces.
Aug 25, 2014 at 6:24 PM
MgSam wrote:
@ByteMR This makes me think- maybe a good way to "fix" the problem without language changes is to build a diagnostic using Roslyn that requires surrounding case statements with braces.
That's actually a really good idea. It would help warn people away from a potential pitfall while making it so the proposal doesn't need to make any breaking changes.
Aug 25, 2014 at 9:30 PM
AlgorithmsAreCool wrote:
How about assignment and equality operators, but with the inverted syntax as my earlier post touched on.
 if (box is Rectangle { Left == 10, int r = Right }) ...
I don't really think that the = operator will create confusion. If people can understand that the calling the is operator is not an invocation of a constructor:
if (box is Rectangle(10, int r))
...and that the 10 is not getting in, but actually getting out and being compared, they can also understand that this is not an initialization,
if (box is Rectangle{ Left = 10, Right = int r }
....but a de-initialization.

Trying to resemble boolean operators and assignments creates more problems than it solves:
if (box is Rectangle { Left == 10, int r = Right }) 
if (box is Rectangle { 10 == Right, int r = Right })  // Also available?
if (box is Rectangle { Left == Right, int r = Right })  // And this?
And even more important, how you let recursive patterns like this:
if (box is Rectangle { Corner = Point{ X = 0, Y = int x } }
And a more philosophical argument, = operator is quite symmetric (unlike Pascal's := for example) so it can perfectly mean a two-directional bind operation, not a right-to-left assignment.
Aug 26, 2014 at 5:40 PM
Edited Aug 26, 2014 at 6:57 PM
Olmo,
The sum of my thoughts is this :
In your first example of object deinitialization syntax you include the following sample:
var p = new Point { X = 1, Y = 2);
switch(p){
   case Point { X = 1,  Y = var y }: //[...]
   case Point { X = var x, Y = *}: //[...]
}
This first line shows the object initialization syntax we know and enjoy today. It clearly shows that the scoped X and Y are being assigned their respective values of 1 and 2. Taken out of context as X = 1, Y = 2 this still makes sense to the eye.

Now the deinitialization syntax first shows X = 1 which is not assigning the value of 1 to X, but in fact guaranteeing that the value of X is equivalent to 1. Although the end result (Y is 1) is the same, the flow of information is obscured by this. After this the section Y = var y is not assigning Y anything, but it is in fact declaring the implicitly typed y and assigning it the value of Y. I would say that this left to right assignment flows contrary to most people's assumptions about assignment in C#. I think it's clearer to simple place the target of the assignment on the left side of the '=' operator.

Now the tricky bit is how to handle the matching/predicating of the entire 'is' expression. I think that allowing the '==' operator would imply that any other boolean valued expressions are also allowed. I think that increases the scope of the syntax, but maybe in a valuable manner.

But removing '==' for the sake of it's misleading implications, leaves us either 'is' or '=' do indicate equality. The out of context expression X is 1 i think reads well, but now the 'is' operator has taken on yet another role. I think the question remains open as to whether it's too much for a single keyword. We then have the expression (also OOC) 1 = X which in has long been a syntax error in just about every C derived language I've ever encountered. It is also worth mentioning that we could borrow the linq contextual keyword 'equals', yielding the statement 1 equals X, I think the only thing wrong with this is the verbosity; imagine : ...Point { 1 equals X, 2 equals Y, 3 equals Z }... it works, but is rather chatty.

I think inverting assignment in the object deinitialization syntax back to the traditional right -> left is very sensible, but leaves a gap for the matching portion of the syntax. A few possible solutions are:
  • increased complication to the 'is' operator : Point { 1 is X, var y = Y }
  • a rather confusing conflation of assignment and equality : Point { 1 = X, var y = Y }
  • a unfortunately verbose : Point { 1 equals X, var y = Y }
  • a one-off hijacking of '==' ala`Point { 1 == X, var y = Y }. But this would invite incorrect suspicion that '!=' and perhaps '<', '>' are also allowed.
  • allowing any boolean valued expression, but i don't think we wanted to go down this road.
These all are not the greatest but the all (excepting '=') work for the recursive pattern and wildcards and none are terribly egregious.

P.S. I think I've conveyed my thoughts about as well as i can, so I'll refrain from evangelizing this further unless specifically asked.
Developer
Aug 26, 2014 at 5:58 PM
AlgorithmsAreCool wrote:
... The out of context expression X is 1 i think reads well, but now the 'is' operator has taken on yet another role. I think the question remains open as to whether it's too much for a single keyword.
Actually, this is almost exactly its existing role, as it has a pattern on the right. In the context of those curly braces, however, the left-hand-side is an identifier for a property rather than a general expression. That is parallel to the object initializer construct, where the left-hand-side isn't any lvalue expression, but names a property.

This is what we've prototyped.
Aug 26, 2014 at 7:14 PM
YES YES YES YES pattern matching YES YES YES YES YES.

C# one language to rule them all.
Aug 26, 2014 at 9:40 PM
nmgafter wrote:
Actually, this is almost exactly its existing role, as it has a pattern on the right. In the context of those curly braces, however, the left-hand-side is an identifier for a property rather than a general expression. That is parallel to the object initializer construct, where the left-hand-side isn't any lvalue expression, but names a property.

This is what we've prototyped.
So, my previous examples will look like:
if (box is Rectangle { Left = 10, Right = int r }) 
if (box is Rectangle { Left is 10, Right is int r }) 
and
if (box is Rectangle { Corner = Point { X = 0, Y = int x } }
if (box is Rectangle { Corner is Point { X is 0, Y is int x } }
For me the new one is slightly harder to parse but I can get used

Will be valid to avoid saying the type? (necessary to mach anonymous types, and almost necessary to mach complex generic types)
if (box is { Corner = { X = 0, Y = int x } }
if (box is { Corner is { X is 0, Y is int x } }
How could a future collection patter look like?
if (collection is List<int>{ is int x, is int y, is 0 } }) //will is be necessary here?
if (collection is List<int>{ int x, int y, 0 } }) //or will be simple

if (collection is { is int x, is int y, 0}) //without type and is
if (collection is { int x, int y, 0 }) //without type
Aug 26, 2014 at 9:47 PM
nmgafter wrote:
johnrusk wrote:
dsaf wrote:
Why are they called "records"? Is this terminology used by any language other than F#? Perhaps writing "immutable class" would make more sense?
I believe a number of languages use the term, e.g. Pascal, to mean "a composite data type with no behaviour". I.e. no methods.

@nmgafter: is it expected that c# records will also be "behaviourless"? If so, most of my concerns about the word "record" would evaporate. If not.... well that's why I find the term confusing ;-). To an ex Pascal programmer like me, "record" means "has no methods".
No. But since Pascal doesn't have composite data types with behavior either, your expectation doesn't really translate to an object-oriented language.
OK, so maybe basing my reply on Pascal wasn't the best idea ;-) I just assumed the Pascal-like meaning of "record" was common, since that is the meaning documented on the wikipedia page for "record".

I guess I just have a nagging feeling that something doesn't smell right, from a C# perspective, in the combination of "record classes", primary constructors, auto-generation of "is"/Equals/GetHashCode, and immutability. Don't get me wrong, I love the convenience of record classes, but... I can't help feeling that the distinction between "record" and "normal" classes doesn't smell right. In particular, "record" feels like a way to tell the compiler "make the primary constructor syntax do what I really wanted it to do all along".
Aug 26, 2014 at 9:55 PM
Edited Aug 26, 2014 at 9:55 PM
nmgafter wrote:
if (box is Rectangle{ Left is int i }) ...
Nice. Personally, I like the consistent use of "is" (rather than equals) here and I like the order (Left is int i). It feels right to me, and is clearly consistent with the role of "is" as the "pattern matching operator".

Will the same thing be supported with var?
if (box is Rectangle{ Left is var i })
Aug 26, 2014 at 10:04 PM
nmgafter wrote:
Anyway, the biggest problem with switch is that one case block "falls through" into another. The only reason for that was consistency with C. I don't see changing that either.
It could be changed if switch statements that did pattern matching used "is" instead of "case" ;-) Why does that help? Because it clearly signals that this is a new-style switch, so new rules can apply without breaking existing code. It also has the nice benefit (as I mentioned above some time ago) that it makes "is" the pattern matching operator everywhere. Which is more consistent than pattern matching with "is" in "if" statements and "case" in switch statements.

I understand the point you made which I raised this earlier, which is that tackling "switch" greatly complicates the internal discussion and approval process for pattern matching. However, it seems like it could be the one best (only?) chance to fix the long-standing issues with the switch statement.

e.g.
switch(expression)
{
   is Val(decimal value): return value;
   is Add(Expr left, Expr right): return Evaluate(left) + Evaluate(right); 
  ....
   is *: throw new ArgumentOutOfRangeException("expression");   // don't need "default". is * will do. 
                                                                                                         // Although default may better for clarity
}

Developer
Aug 26, 2014 at 11:16 PM
I started the spec with a "match" statement separate from the "switch" statement. The differences in scope, control-flow, and semantics were too confusing to be worth the benefit.
Aug 27, 2014 at 8:04 AM
nmgafter wrote:
Actually, this is almost exactly its existing role, as it has a pattern on the right. In the context of those curly braces, however, the left-hand-side is an identifier for a property rather than a general expression. That is parallel to the object initializer construct, where the left-hand-side isn't any lvalue expression, but names a property.

This is what we've prototyped.
Perfect! If match is more expressive than switch we can forget that switch ever existed :)

BTW: now maybe makes sense to speak about expression match?
Aug 27, 2014 at 8:21 AM
nmgafter wrote:
I started the spec with a "match" statement separate from the "switch" statement. The differences in scope, control-flow, and semantics were too confusing to be worth the benefit.
So you are suggesting to introduce a new match keyword after all? That' seems to be the better approach, given the discussion above. Will you update the publicly available spec? And the prototype?

I wonder if you could tell us more about the next steps. When will pattern matching be discussed by the language design team? Will we be informed about their opinions? When?
Aug 27, 2014 at 9:19 AM
As long as match will be used and there won't be any backward compatibility tax, please let the case clauses counterparts introduce their own lexical scope, and let us use braces instead of colons if we want (ie make the colon optional when there's a brace, unless it interferes with parsing the pattern itself).
Aug 27, 2014 at 9:35 AM
Edited Aug 27, 2014 at 9:37 AM
Olmo wrote:
How could a future collection patter look like?
if (collection is List<int>{ is int x, is int y, is 0 } }) //will is be necessary here?
if (collection is List<int>{ int x, int y, 0 } }) //or will be simple

if (collection is { is int x, is int y, 0}) //without type and is
if (collection is { int x, int y, 0 }) //without type
The procedural version of the top example is:
var list = collection as List<int>;
if (list != null && list.Count >= 3 && list[2] == 0) {
    var x = list[0];
    var y = list[1];
    // ...
}
Pattern matching would be shorter code for that, yes, but would you ever write code like that? Working with a list just pulling items out by index and drawing conclusions from their values seems like very obtuse logic that could lead to brittle or error-prone code. There's no clear funneling to expected values and no guarantee that there won't be more or fewer items in the list. You could make partially the same argument about lost context with tuples, I guess, but that's why records are being introduced.

I talked about matching lists earlier in this thread, but only in terms of matching generic types in general and being able to match the list as a whole, for e.g. serialization.
Aug 27, 2014 at 10:45 AM
Edited Aug 27, 2014 at 10:46 AM
I started the spec with a "match" statement separate from the "switch" statement. The differences in scope, control-flow, and semantics were too confusing to be worth the benefit.
If you are introducing a new keyword, why not just make it an expression? In most languages that support pattern-matching, match is an expression.
Aug 27, 2014 at 11:24 AM
JesperTreetop wrote:
Pattern matching would be shorter code for that, yes, but would you ever write code like that?
Yes. This is occasionally useful when you write recursive functions over lists. However, in that case you usually match something like the following (using F# syntax, as I don't know what the C# equivalent is going to be, if any):
let rec func list = match list with
| [] -> () // matches the empty list
| x :: y :: xs -> func xs // matches the first two elements of the list, xs is the rest of list
| x :: xs - > func xs // matches the first element of the list, xs is the rest of the list (though always empty in this case)
Aug 27, 2014 at 11:55 AM
Edited Aug 27, 2014 at 11:58 AM
Expandable wrote:
JesperTreetop wrote:
Pattern matching would be shorter code for that, yes, but would you ever write code like that?
Yes. This is occasionally useful when you write recursive functions over lists. However, in that case you usually match something like the following (using F# syntax, as I don't know what the C# equivalent is going to be, if any):
let rec func list = match list with
| [] -> () // matches the empty list
| x :: y :: xs -> func xs // matches the first two elements of the list, xs is the rest of list
| x :: xs - > func xs // matches the first element of the list, xs is the rest of the list (though always empty in this case)
Doing this kind of operations with typical C# will be really inefficient because:
  • The lack of tail recursion
  • Collections in functional programs are usually linked lists that can be deconstructed in head + rest, while in imperative are contiguous chunks of memory and deconstructing them will mean O(N) operations, or use Slice.
So in C# I also think they matching collection won't be that useful, but there are cases, like matching the arguments of a MethodCallExpression,InvocationExpression, IndexExpression, or equivalent Roslyn nodes.

For example in my LINQ provider I've code like this:
protected override Expression VisitMethodCall(MethodCallExpression m)
{
   switch (m.Method.Name)
   {
          case "Where":
                        return this.BindWhere(m.Type, m.GetArgument("source"), m.GetArgument("predicate").StripQuotes());
    }
}
That could be converted instead to:
protected override Expression VisitMethodCall(MethodCallExpression m)
{
   match (m) //assuming this is the new syntax
   {
       is { Method is { Name = "Where" }, Arguments is { var source, var predicate } }: 
            return this.BindWhere(m.Type, source, predicate);
    }
}
Note: Deconstructing without saying the type is a must!!

But I've to admit that is more code and I miss the assertion that my GetArgument helper method does under the covers.

Other examples could be using JObject but will require support for matching dictionaries.
Aug 27, 2014 at 1:42 PM
Edited Aug 27, 2014 at 1:44 PM
nmgafter wrote:
I started the spec with a "match" statement separate from the "switch" statement. The differences in scope, control-flow, and semantics were too confusing to be worth the benefit.
Also, while you're at it, I'd also like to encourage you to revisit one of the open issues: "Should we have some kind of when (expression) arbitrary filter on cases?". I'd say: Definitely yes. We could introduce a new when keyword like F#, or reuse the if keyword (isn't if better anyway from an English language point of view? Also, if would be consistent with exception filters):
match (expression)
{
is Div(Expr left, ConstantExpression right) if right != 0:  // alternatively: when right != 0
        return Evaluate(left) / right; // no division by zero possible!
is *: // whatever
}
Aug 27, 2014 at 1:48 PM
Edited Aug 27, 2014 at 1:48 PM
nmgafter wrote:
I started the spec with a "match" statement separate from the "switch" statement. The differences in scope, control-flow, and semantics were too confusing to be worth the benefit.
@Olmo @JesperTreetop @Expandable @kekekeks I think you all are misreading this statement. @nmgafter is saying he initially considering using a match statement and chose not to. It is not part of the spec and not in the prototype.
Aug 27, 2014 at 1:54 PM
Edited Aug 27, 2014 at 2:07 PM
Hmm, if we're going to introduce a new match expression instead of enhancing the switch statement, we could probably modify the syntax a bit to make it easier to read and understand:
  • No need for default, is * will do.
  • For simple pattern cases simply returning a value, we could use the lambda operator, similar to the new expression-bodied member syntax.
  • For more complex pattern cases, we could require a statement block, avoiding the switch problem of variable scopes.
  • Note that I defined match as an expression instead of a statement like switch. The types returned by all cases must all be compatible or must all be void. You can assign the result of a match to a variable or return it.
  • The extra parentheses make the syntax more consistent with if statements and exception filters and don't look too bad, in my opinion:
return match (expression)
{
  is (Div(var left, var right)) => "div";
  is (Mult(var left, var right)) if (left != null) => "mult"; 
  is (Add(var left, var right)) if (right != null)
  {
      var world = "world";
      return String.Format("hello {0}", world);
  }
  is * => "unknown"; // default case
}
Aug 27, 2014 at 2:01 PM
MgSam wrote:
@Olmo @JesperTreetop @Expandable @kekekeks I think you all are misreading this statement. @nmgafter is saying he initially considering using a match statement and chose not to. It is not part of the spec and not in the prototype.
That is also how I understood it initially, however, I think there are very good reasons to introduce a separate statement or expression for pattern matching (see my post above). Also, the comment could mean "I just started writing a new version of the spec with a separate match, as all of the differences that have been discussed here make it too confusing to enhance the switch statement.". At least that's how I read it.
Aug 27, 2014 at 3:55 PM
Edited Aug 28, 2014 at 12:00 AM
Expandable wrote:
MgSam wrote:
@Olmo @JesperTreetop @Expandable @kekekeks I think you all are misreading this statement. @nmgafter is saying he initially considering using a match statement and chose not to. It is not part of the spec and not in the prototype.
That is also how I understood it initially, however, I think there are very good reasons to introduce a separate statement or expression for pattern matching (see my post above). Also, the comment could mean "I just started writing a new version of the spec with a separate match, as all of the differences that have been discussed here make it too confusing to enhance the switch statement.". At least that's how I read it.
Agreed. My post started out as a post saying what MgSam said, but after re-reading it I came to land in the other interpretation. I guess only Neal can set things straight.
Developer
Aug 27, 2014 at 8:28 PM
Expandable wrote:
MgSam wrote:
@Olmo @JesperTreetop @Expandable @kekekeks I think you all are misreading this statement. @nmgafter is saying he initially considering using a match statement and chose not to. It is not part of the spec and not in the prototype.
That is also how I understood it initially, however, I think there are very good reasons to introduce a separate statement or expression for pattern matching (see my post above). Also, the comment could mean "I just started writing a new version of the spec with a separate match, as all of the differences that have been discussed here make it too confusing to enhance the switch statement.". At least that's how I read it.
What I meant was that my earlier (unpublished) version of the specification did not modify the switch statement but instead introduced a separate match statement. I believe it would have been more confusing to have two superficially similar but gratuitously different language constructs, so I merged the semantics into the existing switch statement and that is what we implemented.
Aug 27, 2014 at 9:25 PM
Ok, no more examples with match then.

One question Neal. Assuming that pattern matching won't be ready for that VS 2014... could be possible to add at least the is Type ident as an amplification of the declaration expressions?

This is a simple yet super-useful feature and has a non-compromise syntax:
if(obj is string str)
{
     return str.Length
}
Otherwise, by the time pattern matching is published this helper function is going to be in every project:
if(obj.Is(out string str))
{
     return str.Length
}
Aug 27, 2014 at 11:08 PM
Edited Aug 27, 2014 at 11:11 PM
Olmo wrote:
How could a future collection patter look like?
if (collection is List<int>{ is int x, is int y, is 0 } }) //will is be necessary here?
if (collection is List<int>{ int x, int y, 0 } }) //or will be simple

if (collection is { is int x, is int y, 0}) //without type and is
if (collection is { int x, int y, 0 }) //without type
With the new initializer syntax, this should already be possible like so:
if (collection is { Count is GreaterThan(2), [0] is int x, [1] is int y, [2] is 0 })
As for making the type optional in these use cases, we could also just use var or dynamic as appropriate. (Though it certainly does look cleaner without.)

Lists do seem like they could be problematic to match, though. Since an out-of-range index throws an exception instead of simply failing the match, you have to always perform your own bounds checking.

In order to enable a more functional sort of list matching, I think we'd need some sort of common interface. Possibly one of the following.
public interface ICons<T> : IEnumerable<T> {
    T Head { get; }
    ICons<T> Tail { get; } // null if nothing left.
}

public interface ICollectionWindow<T> : IEnumerable<T> {
    T this[int] { get; } // 0 is always the head
    int Count { get; }
    ICollectionWindow<T> Slice(int start);
    ICollectionWindow<T> Slice(int start, int count);
}
Arrays already have ArraySegment<T>, which could easily implement either of these interfaces. The language could special case them to check Tail or Count as appropriate while deconstructing the collection.
Aug 27, 2014 at 11:24 PM
@__Yota__ Isn't Head and Tail just this?
static T Head<T> ( this IE<T> source ) { return source.First; }
static IE<T> Tail<T>( this IE<T> source ) { return source.Skip(1); }
Developer
Aug 27, 2014 at 11:26 PM
Olmo wrote:
One question Neal. Assuming that pattern matching won't be ready for that VS 2014... could be possible to add at least the is Type ident as an amplification of the declaration expressions?
None of this is currently on the radar for Dev14.
Aug 28, 2014 at 2:02 AM
Edited Aug 28, 2014 at 2:23 AM
AdamSpeight2008 wrote:
@__Yota__ Isn't Head and Tail just this?
static T Head<T> ( this IE<T> source ) { return source.First; }
static IE<T> Tail<T>( this IE<T> source ) { return source.Skip(1); }
Enumerables do not support random access, so this would result in the entire collection up to the 'current' point being iterated every time Head were called, which would make iterating through a list this way exponentially expensive as regards to length.

I'm afraid there may be no way to pattern match enumerables without using ToList() first. Once you advance an enumerator, there's no way to back up without restarting from the beginning. C#'s IEnumerator is like D's InputRange, but we'd need something more like D's ForwardRange to be able to use them in patterns. Even in F#, you have to convert them to a list first. (Which happens to be a ForwardRange.)

Back in the day, I used to write an active pattern like the following in F#:
let (|Cons|Nil|) (e:IEnumerator<'T>) =
    if e.MoveNext() then Cons(e.Current, e)
    else Nil

let rec printAll = function
    | Cons(head, tail) -> printfn "%d" head; printAll tail
    | Nil -> printfn "End of Seq"

printAll ({2..4}.GetEnumerator())
This works great for something linear like this, but if there were branching logic, it would fall flat on its face.

I imagine you could implement a similar pattern in C# like so...
public static class ConsEnum<T> {
    public static bool OperatorIs(IEnumerator<T> e, out T head, out IEnumerator<T> tail) {
        tail = e;
        if (e.MoveNext()) {
            head = e.Current;
            return true;
        }
        else {
            head = default(T);
            return false;
        }
    }
}
But you'll likely face the same headache I used to run into. =p
Aug 28, 2014 at 3:37 AM
Edited Aug 28, 2014 at 3:41 AM
Yota What about this way?
Public Structure HeadTail(Of T)
  Private _en As IEnumerator(Of T)
  Public ReadOnly Head As T
  Public Function Tail() AS Nullable(Of HeadTail(Of T))

  Sub New (en As IEnumerator, Head As T)
    _en = en
    _Head = Head
  End Sub

  Function Tail() As Nullable(Of HeadTail(Of T))
    If _en.MoveNext Then Return New Nullable(Of HeadTail(Of T))( New HeadTail(Of T)(_en, _en.Current))
    Return New Nullable(Of HeadTail(Of T))()
  End Function

End Structure

Public Module Exts
 <Extension()>
 Public Function CreateHeadTail(Of T)( source As IEnumerable(Of T) ) As Nullable(Of HeadTail(Of T))
    Dim en = source.GetEnumerator
    If en.MoveNext Then Return New Nullable(Of HeadTail(Of T))( New HeadTail(Of T)(en, en.Current))
    Return New Nullable(Of HeadTail(Of T))()  End Function
End Module
Think that's the right type
Developer
Aug 28, 2014 at 3:44 AM
AdamSpeight2008 wrote:
  Function Tail() As Nullable(Of HeadTail(Of T))
    If _en.MoveNext Then Return New Nullable(Of HeadTail(Of T))( New HeadTail(Of T)(_en, _en.Current))
    Return New Nullable(Of HeadTail(Of T))()
  End Function
This Tail function is not idempotent. It will return a different Tail each time it is called.
Aug 28, 2014 at 5:23 AM
Edited Aug 28, 2014 at 5:25 AM
@nmgafter: Bleep that function MoveNext is mutating some internal state on each call. Doh!

I think this one is idempotent.
  Private _IsSet = False
  Private _res As Nullable(Of HeadTail(Of T))

  Function Tail() As Nullable(Of HeadTail(Of T))
    If _IsSet = False Then
      If _en.MoveNext Then
        _res = New Nullable(Of HeadTail(Of T))( New HeadTail(Of T)(_en, _en.Current))
      Else
        _res = New Nullable(Of HeadTail(Of T))()
      End If
      _IsSet = True
    End If
    Return _res
  End Function
That is close to being Lazy(Of Nullable(Of HeadTail(Of T) ) )
Aug 28, 2014 at 2:01 PM
Edited Aug 28, 2014 at 2:05 PM
AdamSpeight2008 wrote:
@nmgafter: Bleep that function MoveNext is mutating some internal state on each call. Doh!
That is close to being Lazy(Of Nullable(Of HeadTail(Of T) ) )
EDIT: Sorry, after closer look at your solution, it looks you're aware of the problem.

The problem is that collection.Skip(1).Skip(1).Skip(1).Skip(1) is actually a chain of 5 IEnumerables<T> so about 5x slower to travel.

Run this gist to see how IEnumerable<T> and recursion creates O(N^2) algorithms really easily: https://gist.github.com/olmobrutall/fa738d2fe8bf48a7766b
Aug 28, 2014 at 2:31 PM
AdamSpeight2008 wrote:
@nmgafter: Bleep that function MoveNext is mutating some internal state on each call. Doh!

I think this one is idempotent.
  Private _IsSet = False
  Private _res As Nullable(Of HeadTail(Of T))

  Function Tail() As Nullable(Of HeadTail(Of T))
    If _IsSet = False Then
      If _en.MoveNext Then
        _res = New Nullable(Of HeadTail(Of T))( New HeadTail(Of T)(_en, _en.Current))
      Else
        _res = New Nullable(Of HeadTail(Of T))()
      End If
      _IsSet = True
    End If
    Return _res
  End Function
That is close to being Lazy(Of Nullable(Of HeadTail(Of T) ) )
Without testing it, I think wou'll still have problems if you do something like this:
var col = new []{1,2,3,4};

var headTeil = col.CreateHeadTail(); 

var headTeil2 = headTeil; 

headTeil.Teil() //returns 2,3,4
headTeil2.Teil() // returns 3,4
because they are mutable structs.

Anyway, you could just convert the IEnumerable<T> to a LinkedList<T>, run your algorithm and maybe convert it back... but it will be inefficient in most of the cases. Also LinkedList<T> has horrible cache characteristics.

I'm not sure of the value...
Aug 30, 2014 at 10:16 AM
I believe it would have been more confusing to have two superficially similar but gratuitously different language constructs, so I merged the semantics into the existing switch statement and that is what we implemented.
So instead of modern and well designed match we are stuck with limitations of switch from K&R C era. It's like not introducing List<T> because we already have ArrayList.
Aug 30, 2014 at 2:41 PM
Olmo wrote:
The problem is that collection.Skip(1).Skip(1).Skip(1).Skip(1) is actually a chain of 5 IEnumerables<T> so about 5x slower to travel.
I know this is a framework issue rather than a language issue, but I wonder what difficulties there would be with having the framework include
interface IExtendedEnumerator<T> : IEnumerator<T>
{
  EnumeratorAbilities Abilities {get;}
  int Move(int amount);
  int CountIfKnown(CountMode);
  IEnumerable<T> EnumerateRemainder(EnumerateRemainderMode);
  ...
}
The existence of such an methods would make it possible for a method like Skip(1) to operate more efficiently on collections whose enumerators supported the above methods efficiently. For example, List<T> could include types List<T>.SubEnumerator and List<T>.ImmutableSubEnumerator, both of which implemented IEnumerable<T> and contained a starting index. Calling EnumerateRemainder on a List<T>.Enumerator would return one of those objects (the type depending upon the mode). If the IEnumerable<T>.Skip(int) extension method used EnumerateRemainder when available, that would then reduce the execution time of cascaded skips from O(N^2) to O(N).
Developer
Sep 8, 2014 at 10:01 PM
I have updated the specification to describe the property pattern, which was suggested in this conversation and is already implemented in the prototype. This is very useful for using patterns on pre-existing types and properties:
if (o is Point { X is int x, Y is int y }) ...
Sep 9, 2014 at 12:00 AM
nmgafter wrote:
I have updated the specification to describe the property pattern, which was suggested in this conversation and is already implemented in the prototype. This is very useful for using patterns on pre-existing types and properties:
if (o is Point { X is int x, Y is int y }) ...
Really nice! It's getting even more C#-ish!

Some more petitions:
  • Consider renaming recursive-pattern to 'is-operator pattern' since 'property pattern' is just as recursive as this one.
  • Could a combination of is-operator pattern and 'property-pattern' be possible, symmetric to a constructor with object internalizer?
HtmlElement element = new HtmlElement(type: "a", id = "myContainer") { Href = "https://roslyn.codeplex.com" };

switch(element){
    case HtmlElement(type: "a", id: var id) { Href = var href }: return string.Format("{0} refers to {1}", id, href); 
}
  • I still think that the = works better that is. The closer constructing and destructing syntax are the better, using is emphasizes the recursive nature of pattern matching, but it this is not necessary for the recursive-pattern, why here?
if (o is Point { X = int x, Y = int y }) ...
  • Using 'property-pattern' without specifying the type will be a big win, as well as using var in type-identifier
if (o is { X = var x, Y = var y }) ..
  • And because asking is free, completely outside of pattern matching but working on this symmetry, is there a reason why object-initializers can not be used on any arbitrary expression? An ambiguity somewhere?
var order = Factory.Create<Order>(){ TotalPrice = 10 }; 
Sep 9, 2014 at 1:02 AM
Edited Sep 9, 2014 at 3:07 AM
I really like the direction where this is going, especially the consistent treatment of the is operator as a "pattern-matching" operator everywhere. The rule is simple: expression is pattern is a pattern-matching test. This is predictable, natural, and does not cause any surprises. Using is in some contexts and = in others would not be as consistent.

Ideally we would also replace case pattern: with is pattern: in the switch operator. In this case is would become the only pattern-matching operator. But I understand that breaking backwards compatibility is undesirable.

(Although... what do you think about introducing both, and having case pattern: and is pattern: be synonyms for each other? Eventually people who want to use pattern-matching will move towards the is syntax, but any old programs that use simple switch and case continue working.)

A minor note. The current spec says:
complex-pattern:
    constant-pattern
    type identifier
    recursive-pattern
    recursive-pattern identifier
    property-pattern
    property-pattern identifier
How about replacing it with:
complex-pattern:
    primitive-pattern
    primitive-pattern identifier

primitive-pattern:
    constant-pattern
    type
    recursive-pattern
    property-pattern
Again, it brings more consistency (now any pattern may be named or not), and enables not-that-common-but-still-useful scenarios such as:
if (Configuration.Settings.Imagine.Longer.Property.Path.WebMethod is "POST" method) {
    // use variable 'method'
}
Also, the C# 1.0 type test expression is type becomes just another instance of (unnamed) pattern-matching. Which is already how it syntactically looks like in the context of this proposal.
Developer
Sep 9, 2014 at 1:15 AM
Olmo wrote:
Some more petitions:
  • Consider renaming recursive-pattern to 'is-operator pattern' since 'property pattern' is just as recursive as this one.
All of the patterns work with "is", and work without "is" when in a switch statement label.
  • Could a combination of is-operator pattern and 'property-pattern' be possible, symmetric to a constructor with object internalizer?
Yes, and I believe it is already implemented. However, there is something very subtle about it that I'm not sure is good: the type from which the properties are fetched is not (always) the type that you name, but is the type that is the first declared parameter of the matched "operator is". So I'm not quite ready to put this into the spec yet.
  • I still think that the = works better that is. The closer constructing and destructing syntax are the better, using is emphasizes the recursive nature of pattern matching, but it this is not necessary for the recursive-pattern, why here?
The recursive pattern doesn't use the "=", which implies assignment.
  • Using 'property-pattern' without specifying the type will be a big win, as well as using var in type-identifier
if (o is { X = var x, Y = var y }) ..
Agreed, but only if we can infer the type from the static type of o. We don't want to use reflection.
  • And because asking is free, completely outside of pattern matching but working on this symmetry, is there a reason why object-initializers can not be used on any arbitrary expression?
This idea has been tossed around. It doesn't have a champion.
Developer
Sep 9, 2014 at 1:20 AM
Skiminok wrote:
...
I like your ideas, but I'm going to hold off on making such changes for now... in part because I'm not working on the implementation.
Sep 9, 2014 at 8:07 AM
nmgafter wrote:
Olmo wrote:
Some more petitions:
  • Consider renaming recursive-pattern to 'is-operator pattern' since 'property pattern' is just as recursive as this one.
All of the patterns work with "is", and work without "is" when in a switch statement label.
True, but all use the is operator defined in the class as an implementation detail. Another possibility will be to call it object-deconstruct-pattern and object-deinitializer-pattern making room for the collection-deinitialize-pattern. Maybe renaming is operator to deconstruct operator could make more sense now too, if you can use is with so many different patterns.
  • Could a combination of is-operator pattern and 'property-pattern' be possible, symmetric to a constructor with object internalizer?
Yes, and I believe it is already implemented. However, there is something very subtle about it that I'm not sure is good: the type from which the properties are fetched is not (always) the type that you name, but is the type that is the first declared parameter of the matched "operator is". So I'm not quite ready to put this into the spec yet.
Fair enough.
  • I still think that the = works better that is. The closer constructing and destructing syntax are the better, using is emphasizes the recursive nature of pattern matching, but it this is not necessary for the recursive-pattern, why here?
The recursive pattern doesn't use the "=", which implies assignment.
But invokes a method without 'ref' or 'out' implying that values are going in, not going out. If collections de-initializers are being considered, I see two consistent possibilities:


Implicit recursion: In this variant, pattern match expression resemble closely to the constructor counterparts, they just work backwards. is and case are just a way of starting the pattern matching expression, just as if or where start a boolean expression, but you don't need to repeat if and where again and again inside a complex boolean expression.
if (o is Point(int x, int y)) ..  //deconstruct
if (o is { X = int x, Y = int y }) ..  //deinitilize object
if (col is {int x, int y}) ..  //deinitialize collection

case Point(int x, int y): ..  //deconstruct
case { X = int x, Y = int y } ..  //deinitilize object
case {int x, int y} ..  //deinitialize collection
Explicit recursion: In another variant, all the recursive patterns require a new is operator to indicate his recursive nature. Its really explicit but at the end looks like a soup of is everywhere. Aditionally, is is promoted against case with no particular reason.
if (o is Point(is int x, is int y)) ..  //deconstruct
if (o is { X is int x, Y is int y }) ..  //deinitilize object
if (col is {is int x, is int y}) ..  //deinitialize collection

case Point(is int x, is int y): ..  //deconstruct
case { X is int x, Y is int y } ..  //deinitilize object
case {is int x, is int y} ..  //deinitialize collection
Current solution: The current solution is a mixture of both, where = is replaced by is because '=' looks too 'in', but passing parameters to a constructor, or a collection intializer is just as 'in' as asigning a property.

I've based my argument in consistency with 'deinitialize collections' but we can imagine how some crazy pattern matching could look for complex number for example:
if(num is (2 + var i))
if(num is (2 + is var i)) // is is also absurd here
  • Using 'property-pattern' without specifying the type will be a big win, as well as using var in type-identifier
if (o is { X = var x, Y = var y }) ..
Agreed, but only if we can infer the type from the static type of o. We don't want to use reflection.
Of course, I expect IntelliSense there too.
  • And because asking is free, completely outside of pattern matching but working on this symmetry, is there a reason why object-initializers can not be used on any arbitrary expression?
This idea has been tossed around. It doesn't have a champion.
Sep 9, 2014 at 3:48 PM
Edited Sep 9, 2014 at 3:50 PM
Good to see this getting attention again!

Olmo wrote:
if (col is {int x, int y}) ..  //deinitialize collection
I don't really like this method of matching collections. In fact, I don't really like this pattern being thought of as a mirror to initializer syntax at all, though it certainly does look similar.

I'd much rather match collection elements with [0], [1], etc. Speaking of which, the updated draft spec does not seem to include the use of these indexers! Was that intentional, or an oversight?

As for matching data of unknown types, has there been any consideration into the behavior of matching against dynamic?
if (obj is dynamic { Name is string s })
Developer
Sep 9, 2014 at 3:54 PM
Yota wrote:
I'd much rather match collection elements with [0], [1], etc. Speaking of which, the updated draft spec does not seem to include the use of these indexers! Was that intentional, or an oversight?
No, and no.
As for matching data of unknown types, has there been any consideration into the behavior of matching against dynamic?
if (obj is dynamic { Name is string s })
No, there has not.
Sep 9, 2014 at 5:49 PM
Looking again at my last post, something else has occurred to me. is currently never matches when the left side is null. (for reference types only)
string s = null;
Debug.Assert(!(s is string));
Assuming this behavior will apply to patterns as well, this could make decomposition rather difficult. The behavior of asserting that a reference is not null in a pattern can be a very good thing to have as well, so I wouldn't really want that to change. This may seem a little odd, but I'd like to propose the following addition:
if (s is string?)
if (obj is Control { Text is string? text })
if (record is RecordClass(string name, string? nickname, int age))
Obviously, ? is not currently allowed on reference types, but it fits quite well in this context. There is precedence to this. Note that an int? will currently always match its type, even when null.
Developer
Sep 9, 2014 at 8:22 PM
You can test for null using
if (o is null) ...
If you want to capture a value that might be null, use var
if (obj is Control { Text is var text }) ...
Sep 13, 2014 at 5:15 PM
ByteMR wrote:
Since this is a feature that I wish C# had almost daily, I decided to pull the branch and compile it and run through some sample programs.

First off was the canonical expression parser. As is seen in this code https://gist.github.com/Mr-Byte/08355f69dde639aa3ea1

This didn't work exactly how I would have expected it to work, as the left and right variables are null in every case, but the Add case. I'm guessing this is due to switch scoping rules because giving each of the variables in each case a distinct name will not cause an exception to be thrown. This seems like an area that can potentially cause a lot of confusion and definitely needs to be looked at if pattern matching moves forward using switch statements for large matches.

Next up are record classes that specify no primary constructor such as https://gist.github.com/Mr-Byte/0a3b36118cb3a3dce13e

The None case really needs no primary constructor, because None generally does not store any data (an empty class, essentially); however that means None generates no is operator override. So the code as shown here will not compile. There's two ways to fix it. One is to specify an empty primary constructor and the other is to remove the () after None in the if statement. While not really a big issue, it feels inconsistent that a primary constructor (even if parameter-less) must be specified to get an is operator to be generate on a record class.

While talking about option types, the following does not compile either https://gist.github.com/Mr-Byte/40e8abb593451ccce2ec

It would be nice if the compiler could infer the class' generic parameter based on the type information provided. I'm not sure if it is possible, but it would be extremely useful in reducing the verbosity of pattern matching when working with record types that are also generic.

If you do specify the generic type, the code compiles and runs just fine.

And while I understand this is a prototype to help flesh out the idea, the following code will completely crash the compiler https://gist.github.com/Mr-Byte/29f556db56aa8c72481e

A record class with no primary constructor and any method specified (in this case ToString, but it crashes with others) seems to be the cause. The method can be either a regular method or an expression-bodied member and the crash will happen. Specifying a primary constructor stops the crash from occurring.

Besides the few issues I've ran into, I'm certainly liking the direction this is taking. It would be nice to be able to "seal" a hierarchy of record classes, similar to how Scala works with sealed abstract base classes when using case classes. This would allow the compiler to know that a type hierarchy can't be further extended and that no default case is required in the switch statement.
I implemented the prototype based on Neal's specification in a short amount of time. It is very normal to see many bugs in this moment. I continue developing the prototype in this repository: https://github.com/semihokur/pattern-matching-csharp

I created the issues myself for the bugs that you've noticed (https://github.com/semihokur/pattern-matching-csharp/issues). For the other bugs, please create an issue in the github repo so that I can fix them very quickly.

You all are also very welcome to contribute to the prototype. I will also use the github milestones soon to show my roadmap (https://github.com/semihokur/pattern-matching-csharp/milestones). My plan is to send the pull requests to the pattern-matching branch in the codeplex after each milestone is completed.
Sep 13, 2014 at 10:43 PM
Nice! Regarding contributions, do you also accept experimental enhancements in the prototype, or are we limited to the current Neal's specification?
I, personally, would love to prepare a PR that experiments with some of the further thoughts on the matter that have been mentioned in this thread.
Sep 14, 2014 at 2:55 PM
semihokur wrote:
I implemented the prototype based on Neal's specification in a short amount of time. It is very normal to see many bugs in this moment. I continue developing the prototype in this repository: https://github.com/semihokur/pattern-matching-csharp

I created the issues myself for the bugs that you've noticed (https://github.com/semihokur/pattern-matching-csharp/issues). For the other bugs, please create an issue in the github repo so that I can fix them very quickly.

You all are also very welcome to contribute to the prototype. I will also use the github milestones soon to show my roadmap (https://github.com/semihokur/pattern-matching-csharp/milestones). My plan is to send the pull requests to the pattern-matching branch in the codeplex after each milestone is completed.
Thanks for the response! I've actually found another issue, so I'll have to make an issue on Github. I also really appreciate the work you're putting into this.
Sep 18, 2014 at 5:28 PM
In my opinion this only adds some syntactic sugar and in some very nitche cases and you shouldn't change old code you should adopt it if the scenario arrives and helps your paradigm, otherwise its just there.

This is similar to exception filters and comes from the need to be able to have F# patterns implemented in C#.

Lets consider the 'is' overload, this construct allows for special filtering by the compiler but is in no way more performant then simply filtering the clause yourself.

E.g

'if (x is Something) return (x as Something).Value > 0'

Becomes something like

'x is (Something, 1)'

The problem anyone failed to realize is that when x is Something via the virtues of something else either via inheritance or interface and those properties are virtual this could be tricky to debug especially when each type may have a implicit or explicit cast overload for Something.

Additionally the 'Record' concept is nothing different than an anonymous type which has a type with the underlying.

E.g. a Record of Something simply provides a wrapper around Record with your properties as immutable and a hashcode. (only again as syntactic sugar this time to support Primary constructors which have another set of gotchas, e.g. when the inheriting type defines a property which is generated by the derivative type, hiding occurs and you end up having to type everything out anyway)

I wish that things which actually need attention were revisited e.g. events (maybe events which can be turned on and off via a property 'Enabled' and the ability to to stop propagation after a certain handler)

Recursive function optimization or a tail keyword to force it to be applied.


Nonetheless interested to see where and how this gets used in reality.