7 ms·
Draft spec for records and pattern-matching in C#
- kodablah 12y agoAppears similar to Scala w/ case classes, but I wish it was more vague and used extractors similar to Scala's unapply. It would make it much more amenable to existing classes.
- Locke1689 12y agoOne thing we could do here is expand the definition of extension methods to include extension operators. I think this makes sense for other reasons. If we do that, you could provide pattern matching for existing classes, even ones from metadata, by simply manually specifying an extension is-operator for the class.
- virtualwhys 12y ago> Appears similar to Scala w/ case classes Similar? Looks exactly the same to me (language syntax aside), immutable properties, hashcode, equals, toString, etc. Kotlin grabbed it as well for their record types, good idea, eliminates great swaths of boilerplate and mutable state at the same time.
- platz 12y agoAre the matches closed, enabling errors or warnings, when all the cases aren't matched on? Otherwise it just seems like some sugar on switch. Also, guarded matches would be nice.
- Locke1689 12y agoThis is in "open questions." Even without language support requiring exhaustion, this would be a pretty obvious Roslyn custom diagnostic to be introduced.
- louthy 12y agoI think if it could be switched between a compile time error/warning then we'd be in a great place. I think even in Haskell you need to switch it on as a compiler option.
- Glide 12y agoThis is some pretty great stuff. Optimizing the null checking pattern will help a lot. It's a language feature that seems obvious after seeing the if let x = something as? SomeType in Swift. I've only been a year away from C# and I was writing 3.5 code at that time. It's kind of amazing seeing how quickly C# has moved from year to year compared to it's closest competitors. I had the same experience when I was looking at web development after a couple years of not really touching it. It's really nice to just take a step back and realize how quickly these things are progressing.
- manojlds 12y agoSorry to say, but writing 3.5 (I hope you mean 3.0) code a year ago mean't you were far behind. I myself feel that C# has not changed a lot in the last 2 years or more. C# 5.0 was released almost exactly 2 years ago, and even that didn't bring a lot of changes ( The biggest was of course async-await and the other one the callerinfo attributes, and that's about it.)
- moron4hire 12y agoasync-await was huge, though. I don't think we've come anywhere near seeing the full consequences of it. And honestly, I'm okay with C#'s release schedule. Fix bugs rapidly and frequently, by all means, but it takes time to learn how to take the best advantage of new semantics. I could really use not having those semantics change immediately after figuring that stuff out.
- zamalek 12y ago> I don't think we've come anywhere near seeing the full consequences of it. If you dig into how it works and really grok it, you can do some outright crazy stuff with it. For example: http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642....
- algorithmsRcool 12y agoI recall Anders giving a talk (at JAOO maybe?) saying that the big jumps in between C# 1.0 -> 2.0 and especially 2.0 -> 3.0 caused some push back. People complained that they were changing the language too fast and it was causing knowledge churn. So 4.0 was more 'targeted' in scope as a result of this feedback. I know dynamic was a 'big' feature, but it was mostly in it's own corner and had little application to most daily code. .NET 4 introduced Task<T> and the TPL which was very important but it didn't see any assitance from C# itself. As to C# 5.0, I am assuming that the managed languages team at MS was well into designing and implementing roslyn and probably didn't see great value is contributing massive features to the old compliler, but Async/Await couldn't, well uhh, wait for roslyn to be finished.
- DanielBMarkham 12y agoWatched a FP demo in Scala last week. As an F# guy, I was curious about the syntax. I probably didn't get it, but what I thought I saw was a bunch of cool FP concepts bolted on top of a pretty long-in-the-tooth OO language. About the time the guy added the third custom DSL, I decided I would never ever code like that. I like C#. I love OO. And I'm crazy about pattern-matching. But why do we think that combining good things from other places, we always get something that's better? Perhaps we should start defining languages by what they don't do? Otherwise, everything will do everything -- and that increases cognitive load on the developers and leads to lots of noob errors by those just starting out. Could somebody explain to me what we would like to achieve with this?
- Locke1689 12y agoPerhaps you'd like to explain why you think pattern matching has any necessary connection to functional programming? Sure, popularized by ML, but otherwise I'm not buying it. We already have switch and type-checking statements. Those are just special cases of pattern matching.
- kyllo 12y agoBut do your "switch and type-checking statements" allow you to implement a single polymorphic function and also give you a helpful compiler error when your switch is non-exhaustive?
- Locke1689 12y agoallow you to implement a single polymorphic function What do you mean by this? And which type of polymorphism?
- hvidgaard 12y agoPerhaps he is talking about parametric polymorphism? In any case, having the compiler tell you that your pattern matching is not exhastive, and even demand that it is, prevents a whole class of problems and enable you to expand your program and have the compiler help you if you forget to add some new case in a pattern matching.
- paf31 12y agoPresumably this doesn't prevent a developer from creating another "pattern class" subclassing some superclass in another package, thereby breaking existing pattern matches?
- Locke1689 12y agoMark all your types sealed. Roslyn can have a custom diagnostic analyzer to yell about unsealed pattern-matched public classes, but I'm not sure if we want to add language support for a closed set yet. Scala allows you to seal a type hierarchy to a single file. That would work, but it seems hacky -- a file feels more like an implementation detail than a language construct to me. For one, the C# spec doesn't mention files at all -- from its perspective all your code may as well be in one giant file.
- sparkie 12y agoC# doesn't allow you to specify both abstract and sealed on the same class - something that would be really useful for implementing closed types. The restriction is artificial and unnecessary though - CIL allows the two to coexist, and it is how the F# compiler implements discriminated unions. I think it would be wise to aim for feature-compatibility with F#, to ensure you can pattern match over types defined in F# from C#, and vice-versa.
- Locke1689 12y agoYou're right of course -- I misspoke. Marking the types internal, not sealed, would allow the compiler to guarantee that all it sees is all there is.
- lindbergh 12y agoIn the meanwhile, here's a little class I made that implemented just enough pattern matching as a fluent interface : https://gist.github.com/neuschwanstein/6c0c9256c1dc5b370011 https://gist.github.com/neuschwanstein/6c0c9256c1dc5b370011. Not pretty, but it works well enough for a pragmatic programmer.
- Strilanc 12y agoOne way to do very case specific pattern matching in C# is to write an extension method that takes a lambda for each case. For example, my maybe type for C# has `Match`: may.Match( () => print("None"), val => print("Some: " + val)) Not very flexible, or performant, but covers the basics of common cases.
- munificent 12y agoI came up with some similar and wrote a long blog post[1] about it several years ago. I never ended up using it in practice because it felt too syntactically weird, but it was a fun little exercise. [1]: http://journal.stuffwithstuff.com/2009/05/13/ml-style-pattern-matching-in-c/ http://journal.stuffwithstuff.com/2009/05/13/ml-style-patter...
- louthy 12y agoI created a very similar one to you also, and found the same. It's just a bit too clunky syntactically. The only time I'd use it would be if I'm using it as part of an expression where a (switch or series of if) statement would break the expression up. As well as a generic extension on Object I started adding bespoke Match extension methods to a monads library that I've been working on [1] (which again was all about trying to coerce C# into a more expression based style). So the Option monad Match method would work like so: Func<int> res = (from x in DoSomething() from y in DoSomethingElse() select x + y) .Match( Just: v => v * 10, Nothing: 0 ); Or, the Either monad: var result = (from lhs in Two() from rhs in Two() select lhs + rhs) .Match( Right: r => r * 2, Left: l => 0 ); [1] https://github.com/louthy/csharp-monad https://github.com/louthy/csharp-monad
- kevingadd 12y agoThis is a really nice set of C# improvements that fit together to solve a big problem. I love it. I do have some minor questions, though! The simplest improvement that confused me at first is the ability to declare a new inline local to handle an 'out' variable: int x; foo(out x) becomes foo(int x) This by itself is a quality-of-life improvement but it enables the really nice extraction of values for pattern matching. I'm happy to see this as a general language feature instead of a hack specific for records. (Was this already introduced in a proposed future C# version, and I missed it?) The record classes considerably simplify the creation of simple data structures, trees, etc. This alone would kill thousands and thousands of lines of pointless duplicate code in JSIL, so I'm really happy to see it. Are the properties of record class instances read-only? (It's hard to tell from the draft spec. I'd be okay with mutable-by-default with opt-in readonly, but always-readonly would also be okay with me as a simpler design.) The introduction of an overloaded 'is' operator that accepts user-defined arguments on the right-hand-side. You define the operator on a given type and give it out-parameters (and presumably it can accept value parameters, as well, so you can do more sophisticated matching), and the compiler converts that into a call to the matching function that, if successful, yields values from the match. Really clever way to introduce this - the syntax is slightly confusing, but it avoids introducing new keywords and almost all of this feature follows nicely from existing C# features. And switch statements are expanded to be able to pattern-match! Lovely! This is a natural improvement given that switch statements are already compile-time sugar for dictionary lookups when you're switch()ing on non-integer values. It seems like all of this is compile-time sugar that doesn't require any runtime changes. Is that true? If so, what version of the runtime do you think will be the minimum for Roslyn output that uses these features?
- keyle 12y agoIt's interesting that I'm learning Groovy at the moment and I seem to see a lot of Groovy goodies in this draft. such as any.of?.these?.could?.be?.nullhey() or the record class very close to Lua.
- manojlds 12y agoThose kind of constructs have been there in many languages. Mostly, C# can be said to borrow stuff from its cousin F#.