13 ms·
C# 8: Switch expressions
- rattray 8y agoThis might be better titled "C# 8: switch expressions" (edit: it was previously "statement", as the author's post is titled). Or actually, to bait a few more hn clicks (and provide a fuller description): "Pattern matching in C# 8 with switch expressions"
- jmnicolas 8y agoThis might be better titled "changing things for the sake of changing things and appealing to the programming language geeks". The new syntax is not intuitive, not more readable, just a bit more terse.
- arethuza 8y agoActually, I think it is rather intuitive and readable - but then again I programmed in lisp for years.
- noblethrasher 8y agoFor some time, I've thought that the direction of C#'s evolution makes a lot of sense if you look at as the designers' effort to cope with Haskell envy (pattern matching, type inference (which is really just class inference), and monad comprehensions). But, it makes almost complete sense if you instead look at it as coping with Lisp envy. I'm still amazed that they managed to sneak in function literals and FEXPRs (Linq), a metaobject system (dynamic + DLR), a limited form of call/cc (aysnc/await), first-class metaprogramming (Roslyn -- albeit more in the style of Smalltalk rather than Lisp), and a REPL.
- oblio 8y agoI don't think it's Lisp envy. They're porting stuff from F#, which as far as I know is from the ML family.
- noblethrasher 8y agoWell, to be clear, I was being tongue-in-cheek when calling it "envy". I just mean that C# looks messy from the point-of-view of an ML-wannabe, but pretty elegant as an aspiring Lisp. And I say this as someone that is fluent in F# and prefers functional programming in the style of ML rather than that of Lisp.
- falcrist 8y agoThis change was definitely inspired by F# (or ML ultimately). It's far from the first feature to be brought over. Look at async/await, usable tuples, lambda functions, range syntax, etc.
- moomin 8y agoAFAICT, this is kind of true for a lot of F#: F# is where they do the experimentation, C# is where they do the more heavily engineered "fast" version.
- falcrist 8y agoThat seems to be true at least to some extent. However, there are some noteworthy parts of the two languages that seem to be intentionally different. For example, F# optimizes tail recursion while C# doesn't.
- deleted 8y ago[deleted]
- pjmlp 8y agoLINQ actually came from Haskell. "Confessions of a Used Programming Language Salesman, Getting the Masses Hooked on Haskell" http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.868&rep=rep1&type=pdf http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72....
- chrismeller 8y agoThis actually seems to be somewhat of a trend in C# as of late. Ever since they introduced lambdas, really. While I use them every day, my favorite is still “replacing” String.Format with string literals. Shorter, sure, but less intuitive particularly for people coming from another language. You can do roughly the same thing in PHP with backticks, but most people avoid them for the same reasons. I’m not sure how I feel about the trend as a whole. As long as I can continue using the old methods as well I suppose I don’t have much room to complain, but sometimes it does feel like we’re just changing for the sake of change and that’s usually a bad indicator.
- maxxxxx 8y agoI think string interpolation is one of the best things they did to C#. String.Format is pretty error prone.
- kolbe 8y agoI'm on your side. I've found very little use for most improvements since C# 4, but I always use literals now. Things like value tuples and string literals are huge improvements to my code's readability and flow.
- wongarsu 8y agoUsing switch as an expression makes sense, just like using if as an expression (often written as a?b:c) does. It makes it obvious that all you are doing is changing one variable, and it makes initialising constants a lot cleaner. Once you start making switch expressions, the break keyword makes no sense. And with pattern matching the case keyword technically wouldn't be nessesary for normal switch statements already, might as well drop it for the new syntax. I find this completely reasonable, and I think it's very readable, especially compared to the closest equivalents: putting a single switch in a function and calling that, or using nested tertiary operators: const sqlOp = op switch { "&&" => "AND", "||" => "OR", _ => throw new NotSupportedException() } vs function getSqlOp(string op) { switch op { case "&&": return "AND"; case "||": return "OR"; default: throw new NotSupportedException(); } } // ... far away const sqlOp = getSqlOp(op); vs const sqlOp = op=="&&" ? "AND" : op=="||" ? "OR" : throw new NotSupportedException();
- kibwen 8y agoThe break keyword in switch statements makes sense only if the programmer considers switch as a syntax sugar for goto, which are the semantics in C. Once you have a language that doesn't use goto but that still requires breaking each switch case, the only rationale is inertia for people familiar with C. (That said, C# does have goto, right?)
- wongarsu 8y agothe break keyword in switch statements also allows fallthrough, allowing for a terser way to write state machines, at the cost of making the whole construct less intuitive and readable. In a switch expression of course that is moot since break is a statement, and statements can't appear in expressions.
- kibwen 8y agoThe thrust of my above comment is largely about how the idea "enabling fallthrough" demonstrates how poorly the switch statement is usually taught: most people have an intuitive grasp of it as an alternative to if/else if, when in truth it's sugar for a jump table. Break exists to prevent fallthrough rather than enable it! And indeed fallthrough isn't useless, though decades of long experience has demonstrated that it is the wrong default, and newer languages like Go do better by making break the default and having a dedicated fallthrough keyword for when you want it.
- eckza 8y agoMore like the trend of “bolting everything they’ve ever heard on to the language spec”. This is a half-assed, syntactically ugly implementation of case statements in functional languages like Elm or Haskell - the point of which is to cover all possible executions of a branch explicitly, to prevent runtime errors.
- porpoisely 8y agoIt's not simply a matter of "uncomfortable" syntactic sugar. It's a fundamental change from a statement to an expression. Also, there is an elegance to the functional style of pattern matching. And C# has been incorporating a lot of functional paradigms for more than a decade now which may initially feel "out of place" but ultimately benefited the language greatly.
- chrismeller 8y agoAgreed. I was wondering what they could have possibly changed in ye olde switch statements we’ve all been using for decades. Edit: ye, iOS, ye!
- Multicomp 8y agoYeah this is a feature ported from fsharp / F#, but somewhat watered down a bit to play nice with C#'s existing architectural choices.
- flafla2 8y agoThe lineage can be further traced back to the first ML languages such as SML, and even further back to LISP and RegEx-based languages. https://en.m.wikipedia.org/wiki/Pattern_matching https://en.m.wikipedia.org/wiki/Pattern_matching
- jakear 8y agoTo me, all these IIFE’s make a good case for adding a block-as-expression construct. Either take from Scala, where every block evaluates to the last expression executed in that block (and every statement is an expression); or maybe introduce some new syntax (‘do { .... }’ maybe?) if the prior would be a breaking change.
- zmj 8y agoThere's a bytecode representation already - you can use it when compiling to IL: https://docs.microsoft.com/en-us/dotnet/api/system.linq.expressions.blockexpression?view=netframework-4.7.2 https://docs.microsoft.com/en-us/dotnet/api/system.linq.expr... What's missing is a way to express an anonymous block expression in C#. I'm not sure that's a gap worth filling, though - if the logic is worth a block, isn't it worth a name?
- twhb 8y ago“If” statements and loops already make use of anonymous blocks, and I think serve as ample demonstration that, no, not all blocks are better off named. What’s missing is the ability to use those blocks wherever an expression is expected.
- platz 8y ago// now you kind of enforced to // handle all values, otherwise // it will not compile I fail to see how it will not compile if all values are not handled. C# switch is not traditional pattern matching, because you don't get compile time feedback. Claiming that is does is disingenuous.
- nikeee 8y agoIn TypeScript, this can be achieved using an assertion to the never type. I don't think it exists in C#, though.
- platz 8y agoThey aren't real guarantees in TypeScript, because the type system doesn't guarantee anything about the actual values that are flowing through the system. i.e. you have to code everything in TypeScript and make sure everyone follows the best practices.
- Dylan16807 8y agoMost statically-typed languages don't verify types at runtime either. As long as you enforce types at the boundaries between typed and untyped code, you should be fine. And enforcing invariants at the boundaries of your code is pretty important for all languages.
- platz 8y agothe point is that in FP languages, pattern matching uses closed ADTs, so you always know at compile time if you've covered all the cases.
- Dylan16807 8y agoHuh, you have do something with returning from a switch in typescript if you want it to make sure all cases are covered. So it's close but not quite there.
- azhenley 8y ago"+" => ((Func<int>)(() => { Log("addition"); return a + b; }))(), This is incredibly ugly. Why couldn’t it be “+” => {stuff}
- LandR 8y agoThat is ugly. What he's doing is creating an anonymous function which takes no parameters and returns an int. That's what a Func<int> is, and then he is executing that function, that's the () bit after })) He could write it nicer as: "+" => Add(), Where he defines add as int Add() => a + b; He's just doing too much inline, 99% of C# developers would not write code as he has done there. Its' the bloggers code style that is the problem here, not the new switch syntax. Although he probably did it to make the example more self contained and terse.
- rattray 8y agoI agree with your conclusion, but let's keep the conversation civil.
- Dylan16807 8y ago> He's just doing too much inline It's two short lines. The problem is that he's writing a small but more than one line bit of code, so the overhead of the anonymous function is just as big as the code itself, and arguably more complex.
- crashdelta 8y ago
- pron 8y agoJava is also getting switch expressions next month (as a preview feature in JDK 12): https://openjdk.java.net/jeps/325 https://openjdk.java.net/jeps/325
- chvid 8y agoIt is exactly the same isn't it?
- dmitriid 8y agoIt's not. C#'s switch can pattern match on value and type: https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-with-patterns-in-c-8-0/ https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-w... Java's switch still operates only on String, int, short, byte, char, and their wrapper types: https://blog.codefx.org/java/switch-expressions/ https://blog.codefx.org/java/switch-expressions/
- jermaustin1 8y agoI'm not sure what this accomplishes over a normal ugly switch.
- mgamache 8y agoit's not ugly...
- ben509 8y agoFor various reasons, probably due to linguistics, programmers will often make dumb compromises just to avoid writing an extra few lines of code. Many of these constructs (see especially Python's new walrus operator) are trying to let people express things in the way they want so they'll write better code.
- recursive 8y agoYou could use it as an argument to a chained base() constructor.
- doodpants 8y agoThe new switch expression is to the old switch statement what the ?: operator is to the if/else statement. Sometimes you want to provide one of two different values based on a condition. If you use if/else, then each statement body has to contain either an assignment (if the value is to be used later in the same function), or a return (if the value is to be immediately returned from the enclosing function). It is often more clear and concise to use a ?: expression at the site where the value is to be used. Now consider the case where you need to choose among three or more different values. You can use chained if/else statements, which have the same drawback of needing to contain either assignments or returns. Or you can use chained ?: expressions, but there's really no good way to format this in a way that remains clean and easy to understand. The new switch expression provides a cleaner alternative to chained ?: expressions.
- deleted 8y ago[deleted]
- kentosi 8y agoThough I'm sad that a lot of developers dislike scala, I'm certainly glad to see some of it's* good parts being incorporated into current and new languages: Pattern matching to various degrees in Kotlin, now C#, and soon Java 12. * I acknowledge my ignorance in thinking that scala was the first one to the table with this.
- xiphias2 8y agoThis makes sense together with algebraic data types, but I'm not sure if that's implemented.
- eckza 8y agoThey’re saving ADTs for 9.0, at which point they’ll beat their chests and loudly proclaim them to be the Best Thing Ever of All Time.
- moogly 8y agoMore and better examples here https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-with-patterns-in-c-8-0/ https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-w...
- danielovichdk 8y agoI am not sure I like the path c# is going down. For years the language has been trying to evolve from being aesthetic and easy to understand and read. With the functional implementations and patterns I believe the language is trying to embrace too much. This is not specific to the swith expression in this context but in general. You can write C# in too many forms and I don't believe that's really good.
- dgritsko 8y ago> You can write C# in too many forms and I don't believe that's really good. Why not? And what, exactly, defines "too many"?
- azhenley 8y agoPerhaps he means something like Python's motto of "There should be one--and preferably only one--obvious way to do it." https://www.python.org/dev/peps/pep-0020/ https://www.python.org/dev/peps/pep-0020/
- goto11 8y agoI think all the new features are improvements, but I agree the language is getting big with many ways to do the same thing. I don't think MS will ever do a 'Python 3' and cut backwards compatibility.
- kminehart 8y agoI know people meme a lot about Go and "not having generics," but I really feel that a lot of the things left out of Go have benefitted it. I've never really been able to jump into a project without a lot of mental overhead. With Go, most code everywhere is written pretty idiomatically, making it easy to approach and read. I've heard similar things about C#, but my experience hasn't been as great. This is a big problem in the JavaScript world in my opinion. So many different ways to solve common problems that it's difficult to approach. OP might be worried this could be happening to C#.
- mal10c 8y agoIn my opinion, I find the Tuple patterns the most interesting way to use Switch Expressions. But (and maybe I'm stuck in my ways), I'm kind of on the fence in terms of using this. On the one hand, it shortens some code, but on the other, it's more difficult to read than an if statement (in the case of tuples). I guess I'm not completely sold on this yet. I know the big argument in the article was that this is a way to ensure your variable has a value when you go to use it. When I hear things like that, my first reaction is "then write better code if you're running into that problem."
- gambiting 8y ago"The new switch expression is quite simple. Anyone familiar with the switch statement can say what the following code is doing" Uhmmmm....I'm a professional C++ programmer who uses switch statements quite a lot and no, I couldn't instinctively tell what it was doing straight away. Am I being dumb?
- kiliancs 8y agoThis is clearly referring to C# switch statements. Not that they are very different from switch statements in most languages anyway.
- ken 8y agoThis is perhaps the most obvious example yet of what PG described as "taking features from Lisp and gluing them to C". That's not necessarily a bad thing -- this is a great and useful feature, and one I frequently miss when using C-family languages. I wonder, though, at what point a programming language collapses on itself, from too much syntax. I haven't used C# since the 4.x days, and I recall back then it was already on the verge of being too complex for me to fully understand. Admittedly my personal threshold of complexity is lower than most programmers', but this is a really big language. Do they plan to keep adding syntax indefinitely? At some point, it's going to have to get eclipsed by something else. The next generation will be as effective a tool, for the kinds of things most people want to make, and not too complex for most people to understand. Perhaps part of that would consist of just using expressions for everything from the start.
- ramenmeal 8y agoThis is my feeling as well. I suggested that a c# lite or a rethinking of c# would be awesome. I got downvoted to oblivion though. But I do think that there's too much mental capacity used just reading c# if the code really uses all the features of it. I like go lang cause of this, but go is a bit too far on the other side of the spectrum for me.
- yarg 8y agoHaving something more akin to typescript's strict compilation might be more reasonable. You'd mark out older syntaxes/APIs as restricted or deprecated and compilations would yield warnings or errors (when those features were used) depending on the compiler level. This would allow new projects to be developed using only the newer (standard) syntax, while allowing a transitional vector for older projects.
- xkapastel 8y ago> I suggested that a c# lite or a rethinking of c# would be awesome. "a rethinking of C#" is a good description of F#.
- zamalek 8y ago