-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Discussion]: match construct for pattern matching #5016
Comments
I would propose to replace |
Agree with most of this. I think this is a lot better than trying to re-appropriate the public DogEnum StringToDog(string s)
{
switch(s)
{
case "Lab": return Dog.Labrador;
case "Collie": return Dog.Collie;
}
} Though an expression form would work just as well here, it seems worthwhile to support both use cases. |
It's mostly to keep consistency with the expression form of |
@HaloFour, I hope you don't mind if propose a different syntax for the match expression.
Another solution to the select case ( ... )
{
case ... :
while ( ... )
{
foo = match ( ... )
{
|: ... => exit while; // Executes code from point after the enclosing while block.
|: ... => exit select; // Executes code from point after the enclosing select block.
|: ... => continue while; // Execution continues at the enclosing while.
|: ... => continue select; // Execution continues at the enclosing select.
|: => null;
}
}
} for match statement (see #5037) |
With a Expr Simplify( Expr e )
{
return match ( e )
|: Add(l,r) => match(l,r)
|: (Const(0),*) => Const(r);
|: (*,Const(0)) => Const(l);
|: (Const(a),Const(b) => Const(a+b);
|: => Add(Simplify(l),Simplify(r));
|: Mul(l,r) => match(l,r)
|: (Const(0),*),
(*,Const(0)) => Const(0);
|: (Const(1),*) => Const(r);
|: (*,Const(1)) => Const(l);
|: (Const(a),Const(b)) => Const(a*b)
|: => Mul(Simplify(l),Simplify(r)); ;
|: Sub(l,r) => match(l,r)
|: (Const(0),*) => Const(-l);
|: (Const(a),Const(b)) => Const(a-b);
|: => Sub(Simplify(l),Simplify(r)) ;
|: Div(l,r) => match(l,r)
|: (*,Const(0)) => ERR_DivideByZero;
|: (*,Const(1)) => l;
|: (Const(a),Const(b)) => Const(a/b);
|: => Div(Simplify(l),Simplify(r));
|: => e ;
} |
@AdamSpeight2008 Ooo, I forgot guard blocks. I'm honestly not a fan of the |
VB.net's verbose verbiage and more powerful Function Simplify( e As Expr ) As Expr
Return _
Select( e )
Case Add(l,r):
Return Select Case (l,r)
Case (Const(0),*): Return Const(r)
Case (*,Const(0)): Return Const(l)
Case (Const(a),Const(b)): Return Const(a+b)
Case Else: Return Add(Simplify(l),Simplify(r))
End Select
Case Mul(l,r):
Return Select Case (l,r)
Case (Const(0),*), (*,Const(0))
Return Const(0)
Case (Const(1),*): Return Const(r)
Case (*,Const(1)): Return Const(l)
Case (Const(a),Const(b)): Return Const(a*b)
Case Else: Return Mul(Simplify(l),Simplify(r))
End Select
Case Sub(l,r):
Return Select Case (l,r)
Case (Const(0),*): Return Const(-l)
Case (Const(a),Const(b)): Return Const(a-b)
Case Else: Return Sub(Simplify(l),Simplify(r))
End Select
Case Div(l,r):
Return Select Case (l,r)
Case (*,Const(0)): Return ERR_DivideByZero
Case (*,Const(1)): Return Simplify(l)
Case (Const(a),Const(b)): Return Const(a/b)
Case Else: Return Div(Simplify(l),Simplify(r));
End Select
Case Else
Return e
End Select
End Select
End Function |
This proposal conflates statements and expressions. It gives a grammar for a statement form and then proceeds to give an example of its use as an expression. I cannot really comment until it pulls itself together. |
@gafter , which grammar? You can always ignore the return value of a function call, so in effect makes the |
@AdamSpeight2008 the proposed grammar follows the section entitled "syntax" in the original post of this issue. The grammar provides support for a statement form only. Your grammar does not really make sense to me. I do not know, for example, what a LineOfCode is, and your grammar does not allow anything between any of the parentheses. |
@gafter updated the grammar. |
@gafter I'm not particularly versed in writing grammars so please excuse errors, inconsistencies or shortcomings (for the time being). I proposed my grammar basically as a starting point since there hasn't yet been any proposals specifically for an expression form of pattern matching. I am also more than willing/happy to look at the expression form and statement form separately. I think we'd be in agreement that the former is the more useful of the two anyway given that I based the syntax in the original comment on how it might feel to call one of the two following methods passing a list of lambdas replacing the empty argument list with the pattern to be matched: // expression form
public static T match<T>(object expression, params Func<T> matches);
// statement form
public static void match(object expression, params Action matches); e.g. string result = match(expression,
() => "foo",
() => {
return "bar";
},
() => throw new InvalidOperationException()
); Good, bad or indifferent I thought it would provide a decent starting point and it should somewhat be syntax that C# developers are familiar with. |
@HaloFour I believe any new compound statement form for pattern matching that does not support returning from the enclosing method is a non-starter. Similarly I do not think we would want a new expression form in which subexpressions could include control statements (especially those that interact with enclosing statements). If we did want to add support for statements in expressions, pattern matching is a very strange place to introduce them. That is why I believe that pattern matching constructs for expressions and statements must be separate. I am working on |
I agree with this. It doesn't make sense to allow the middle of a
I can understand this. While I was borrowing from the My gut reaction is that not permitting this form of multiple-statement expressions with the syntax permitted by lambdas today is unnecessarily limiting, however with guard blocks it might not be that necessary. The one really nice thing about disallowing it would be that those control statements would no longer come into play so the conversation about what |
Here an alternate grammar. I am thinking specifically of the use of
An example, although it is nearly identical to the first example above: var area = match (shape) {
Point(*, *) => 0,
Line(Point(*, *), Point(*, *)) => 0,
Square(var width) => Rectangle(width, width),
Rectangle(var width, var height) => width * height,
Circle(var radius) => (radius * radius * Math.PI),
* => throw new InvalidOperationException("Unrecognized shape!")
}; |
When I think about a new match statement the most important aspects I can think of are:
Familiarity means it has to look and behave like an existing construct: To explain familiarity better, none of these constructs is an expression. This means that if we want Statement form that looks familiar to an match (*expression*)
with (*pattern*) *statement*
with (*pattern*) *statement*
default *statement* match (p)
with (Line l)
DrawLine(l.Start, r.End);
with (Rectangle r) {
DrawLine(r.TopLeft, r.TopLeft.AddX(r.Width));
...
}
default
throw new ApplicationException(); Some additions that could improve the syntax:
What about expressions? As I've said, most constructs that we can base match (*expression*) {
with (*pattern*) => *expression*;
with (*pattern*) => *expression*;
default => *expression*;
} var area = match (p) {
with Line => 0;
with (Rectangle r) => r.Width*r.Height;
default
throw new ApplicationException();
} Have you seen what I've done? I've mixed expressions and statements! To make this safe, we need the following rule:
I'll try and post a more formal grammar a bit later. |
@orthoxerox I do not think we would want a new expression form in which subexpressions could include control statements (especially those that interact with enclosing statements). If we did want to add support for statements in expressions, pattern matching is a very strange place to introduce them. I am working on extending the existing |
@gafter but throwing because no pattern matched the given value is a legitimate use case. What would you propose, use a helper method |
I propose |
@gafter : How? What is the return type? ;p |
@vladd: |
@leppie: It doesn't but the compiler can interpret |
@HaloFour I don't think we can get the |
@gafter: what will happen if I write |
@orthoxerox it seems like you should get some kind of diagnostic for that. |
Discussion moved to #5143 |
It's been mentioned several times in the different pattern matching threads (#206, #2136, #4781, #4944) so I thought I'd at least get the ball rolling with a proposal to introduce a
match
construct which would be specifically suited for pattern matching. I more or less yoinked this syntax out of a hat and I am not married to it so feel free to suggest alternatives. I chose to follow the lambda syntax fairly closely as I think that most C# developers should be fairly familiar with the rules of writing lambda expressions and anonymous function bodies so the behavior of encountering areturn
within the body should not be confusing.Syntax:
The syntax would be as follows:
I've attempted to adopt this grammar from the
switch
syntax in §8.7.2 of the C# 5.0 specification, feel free to correct.The
match
statement can be used as both a statement and an expression. In the case of being used as an expression the inferred result type of each of theanonymous-function-body
s within eachmatch-section
must be inferred to the same type which is the result type of thematch
expression itself.The
match
statement must be complete meaning that all possible patterns must be checked otherwise it will result in a compile-time error.Differences between
switch
andmatch
in statement form:match-section
. This applies to both identifiers use withvar
patterns as well as variables defined within theanonymous-function-body
.match-section
is self-contained and there is no automatic fall-through. In short, it is not necessary tobreak
out of a section.match
statement. Thereturn
statement instead acts as a way to break out of the currentmatch-section
.Examples:
match
as an expression:When using
match
in an expression the result type of each of the expressions or anonymous function bodies must be inferred to the same type. That type cannot bevoid
.match
as a statement:When using
match
in a statement the result type of each of the expressions or anonymous function bodies must be inferred to returnvoid
.In the case of a simple
expression
the inferred result type can be anything but will be discarded, similarly to how you can assign a lambda that calls a function that returns a value to anAction
delegate.This is the equivalent
switch
statement:The text was updated successfully, but these errors were encountered: