Skip to content
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

Split the features/patterns branch into two branches for subfeatures in/out C# 7 #10866

Closed
gafter opened this issue Apr 25, 2016 · 18 comments
Closed
Assignees
Labels
Area-Compilers New Language Feature - Pattern Matching Pattern Matching Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented

Comments

@gafter
Copy link
Member

gafter commented Apr 25, 2016

The features/patterns branch needs to be split into two branches for those features we are reasonably certain will be in C# 7 and those features that might be delivered in a later release. The former can be integrated into the future branch and the latter can remain in the patterns/feature branch.

Part 1 (targeting future):

  • Scoping implementation in support of pattern variables (also needed for the "out var" feature)
  • expression is Type identifier
  • case Pattern when expression: with the following pattern forms
    • Type Identifier
    • var identifier
    • Constant

Part 2 (to remain in features/patterns):

  • all recursive pattern forms, including
    • positional patterns
    • property patterns
    • tuple patterns
    • *
  • let statement
  • match expression
  • throw expression
  • pattern-matching based on user-defined code
@DavidArno
Copy link

DavidArno commented Apr 26, 2016

No!!! Please don't leave match expression out of the C# 7 release!

With all the experiments I've done so far with patterns, I've probably used match 90% of the time and switch only 10%; if that much. Pattern matching is a functional feature and functional programming is all about expressions; not statements. Having pattern matching only in switch would be a weird addition to C# that I'd imagine most functional-style C# developers would just ignore and most OO-centric developers will just not understand or use. It would be a hugely wasted opportunity. If match can't make it into C# 7, then there's no point in releasing pattern matching at all in that release, IMO.

@alrz
Copy link
Member

alrz commented Apr 26, 2016

I think it makes more sense to also postpone var patterns because without recursive patterns there ain't no real use for var patterns alone. Honestly, I think the team could deliver a more sophisticated value binding pattern overall, but shipping var patterns would close that case right away. :(

@gafter
Copy link
Member Author

gafter commented Apr 26, 2016

@DavidArno Your feedback on the prototype that results from these changes will be helpful in our planning.

@gafter
Copy link
Member Author

gafter commented Apr 27, 2016

@alrz A var pattern is useful as the last case of a switch statement. I'm not sure what you have in mind with a "value binding pattern".

@DavidArno
Copy link

DavidArno commented Apr 27, 2016

@gafter,

I'm not sure that my feedback will change from now. It's difficult to articulate the frustration and sadness I feel over the Remove evidence of advanced pattern-matching features for C# 7 commit. But I'll have a go...

As someone who uses C# to write functional-orientated code, the experience is a difficult one. A lot of the time, I seem to fight the language, rather than working with it. So when I first came to this site and saw the list of features that potentially might make it into C# 7, I was extremely excited: tuples, records, pattern matching were all up there at the top of the list. These were exactly the sort of features that I wanted added to the language. Exciting times lay ahead.

Since then, I've seen things steadily go downhill:

  • Implicitly typed lambdas would have been a great new feature; instead we got local functions, which only address some of the use cases of the former requirement.
  • It looks like will get tuples, but they will be mutable. The team have offered lots of reasons why mutable tuples "aren't that bad really", but the fact remains: they will be mutable, when they shouldn't be.
  • Records won't make it into C# 7.
  • Discriminated unions/ADTs won't make it into C# 7.
  • A pattern matching version of switch will likely be offered; but I don't use switch, so this has no value to me.
  • There will be no pattern matching expression support in C# 7, which effectively means it won't support pattern matching as matching expressions is the main use I'd have for pattern matching.
  • The is Type var feature looks like it will make it in to the next release, but since "pattern-matching based on user-defined code" won't, that suggests custom is operators won't be supported. Thus this becomes a minor syntax improvement feature; rather than a pattern matching one. Useful, but hardly exciting.
  • The let keyword, supplying immutable vars, won't make it in C# 7, and I suspect the "never make breaking changes, ever!" camp will win on this and it never will make it in to the language.

What we will likely get are:

  • out var support. out parameters are an anti-pattern in my world. Making it easier to use this anti-pattern isn't therefore a feature I'm going to get excited about, other than in a negative sense.
  • Code generators. Apparently people have been making a lot of noise about this. Clearly I mix in different circles to these folk as I only heard about it a couple of weeks ago as a possible way of working around the lack of record and DU support in C# 7. It unfortunately necessitates using partial types (again a feature I steer well clear of as it massively reduces code discoverability), but I could have lived with that. However, with no real pattern matching and no custom is operators, there seems little point in creating those work-arounds. So again another feature I don't care about.

C# 7 started out appearing to be a super exciting release: "C# gets functional". Now though it's appearing to be a complete "meh" release. I'm sure some (maybe even the majority) will be excited by the likely new features, but even the features that do interest me (tuples and local functions) are disappointingly poor implementations of what I'd really like to have seen.

More and more devs are catching the "functional bug". In the .NET world, this normally means switching to F#. Whilst a great language, it's not without its problems, such as having nowhere near the level of tooling that C# devs enjoy. A few months ago, C# 7 looked like it could offer a great set of functional features too, giving people a choice. That dream is well and truly shattered now. Maybe C# 8 will deliver, but maybe it will prioritise other features I'm not interested in (eg private protected - I don't use inheritance, so again a useless feature for me) over delivering real pattern matching, along with immutability, DU's, records etc. So that leaves me, and others in my position, with a frustrating choice: wait maybe another year or two in the hope the features we want appear in a future release, or give up on C# as a lost cause and move over to F#. Going from a high level of excitement to facing this choice over the course of a few months, has been a hugely sobering experience, sadly.

Having said all of that, I am aware that the language team have a difficult balancing act to contend with. They have conflicting demands from the majority who want more of the same and a minority who want radically new features in order to stay with the language. If you meet the needs of the former, you risk failing to stem the tide of leavers. If you meet the needs of the latter group, you risk complaints from the majority that you are pandering to a minority and ignoring what most want. Also, I accept that my "excitement to disappointment" experience is entirely of my own making. You guys have been very clear all along that nothing is set in stone and I've ignored that all along at my own cost. I've tried to offer honest feedback. If it comes across as negative criticism, I can only apologise as that isn't my intention.

@alrz
Copy link
Member

alrz commented Apr 27, 2016

@gafter In the current spec let x = e2 when e2 else stmt; is not possible while let var x = e2 when e2 else stmt; is. Unless you want to take breaking changes, allowing that would complicate the syntax for let statements. I wrote up my thinking regarding value binding patterns in #10624 comment it doesn't allow let var x and replaces var with let in patterns.

@gafter
Copy link
Member Author

gafter commented Apr 27, 2016

In that comment, you wrote

identifier-pattern (has overlap with constant-pattern, see below)

But there is no hint "below" as to what a constant-pattern is, or how the ambiguity would be resolved.

@alrz
Copy link
Member

alrz commented Apr 27, 2016

@gafter I could be more specific. That's nothing new though, Swift uses this semantics so I suppose it wouldn't be a problem, unless you see something that I don't.

switch(e) {
  // x and y are constants as the current spec
  case (x,  y): break;

  // x and y are new variables as the current spec (just `let` instead of `var`)
  case (let x, let x): break;

  // x and y are new variables (`let` distributes to each identifier pattern)
  // -- short for the previous case
  case let (x, y): break;

  // this is not allowed due to the "value-binding patterns cannot be nested" rule
  case let (let x, let y): break;

  // so the following is perfectly fine,
  // an identifier inside a value-binding pattern which introduces a variable
  case let x: break;
}

This wouldn't be restricted to tuples and works for any other recursive patterns in the spec.

switch(e) {
  case let Point(x, y): break;
  // equivalent to
  case Point(let x, let y): break;

  case let Point { X: x, Y: y }: break;
  // equivalent to
  case Point { X: let x, Y: let y }: break;
}

This is specifically consistent with let-statement as it is kind of a "value-binding pattern" so that wouldn't be much of a surprise and there is no need to special case let x = e because you will get it "for free" via identifier patterns.

let x = e;
let (x, y) = e;

@gafter gafter added the Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented label Apr 27, 2016
@gafter
Copy link
Member Author

gafter commented Apr 27, 2016

The production

pattern
    : `let` pattern
    ;

doesn't makes sense to me. Is the idea that once you put let before a pattern, you can't have constant patterns anywhere inside them?

@gafter gafter closed this as completed Apr 27, 2016
@gafter
Copy link
Member Author

gafter commented Apr 27, 2016

This is now done. Part 1 is in future, and Part 2 is in features/patterns.

@alrz
Copy link
Member

alrz commented Apr 27, 2016

@gafter Non-identifier constants are fine, but identifiers will be new variables in presence of let before the pattern. So you will have let (x, y, 1, "") as a shorthand for (let x, let y, 1, ""). There is nothing ambiguous about literals here.

You can't prevent the recursion syntactically, because you might have a let pattern anywhere inside it anyway. So it has to be done semantically.

@wuppy
Copy link

wuppy commented May 6, 2016

I would like to add that match is already the most used C# 7 feature for me as well, about tied up with the new switch (I've utilized both extensively, I always just use the new features for hobby-projects wherever they make sense, then modify the code when VS drops support).

I've had my own libraries for "pattern matching" previously, but having the feature actually incorporated in the language results in far-higher quality code than any implementation I could think of using only existing features.

It won't be that horrible to emulate with switch I guess, but drops the opportunity to use pattern matching in expression bodied methods, where match often makes the most sense now.

@gulbanana
Copy link

does this split have any implications for exhaustiveness checking in switch/match?

@gafter
Copy link
Member Author

gafter commented May 14, 2016

does this split have any implications for exhaustiveness checking in switch/match?

No. switch does not currently do exhaustiveness checking, and we do not expect to change that. match isn't part of the C# 7 side of the split, so there is nothing there to report.

@GSPP
Copy link

GSPP commented May 16, 2016

If match will not be in C# 7 does this mean I have to use a statement to match values? E.g.

int someResult;
switch (input) {
 case ...: someResult = 123; break;
 ...
}

That is so awkward that even nested ?: are better. Also, it can't be a subexpression.

var someResult =
 someCase ? 1234 : 
 someOtherCase ? 5678 :
 ...;

This code is clean but tooling support is notoriously bad for that pattern.

Can we not have a form of match that only supports what's uncontroversial (e.g. what switch does but as an expression). Expressions compose so much better than statements. switch in C was meant as a way to make the compiler generate jump tables (e.g. Duffs Device). It never was meant to do this rather extensive job.

@alrz
Copy link
Member

alrz commented May 16, 2016

I'm pretty sure that match is not totally discarded, it's just postponed for a later version.

@GSPP
Copy link

GSPP commented May 16, 2016

I understand that and it's a cause for great sadness!

Is the list below "What we will likely get are" in this thread exhaustive? That would be like C# 4 in significance which was the least significant release so far.

@alrz
Copy link
Member

alrz commented May 16, 2016

@GSPP Check out #2136.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers New Language Feature - Pattern Matching Pattern Matching Resolution-Fixed The bug has been fixed and/or the requested behavior has been implemented
Projects
None yet
Development

No branches or pull requests

6 participants