9 ms·
How many keywords I can fit into a single C# expression?
- ramenmeal 8y agoC# is a mess. I'd love a C# light.
- maxxxxx 8y agoWhat for? You can write code C# 2.0 style if you want to.
- jasonboyd 8y agoA mess in what way?
- ramenmeal 8y agoSo many features have been added over time, I think it's beginning to show that the features are piled on top of a language that weren't designed for them. Things that I think feel awkward are the tuples handling, switch case pattern matching. As a go developer, when I see the Func, Action, Expression, and Predicate objects... oof I shudder. When you layer function overloading on top of those, you can get in a mess. Obviously it's an unpopular opinion, but I would like to see what c# would look like if it was designed from scratch again.
- maxxxxx 8y agoTo some degree I agree but I am pretty impressed how much they have been able to add while still keeping C# a pretty clean language. I am sure most language designers dream about starting from scratch....
- moron4hire 8y agoQuite the contrary, as someone who has been doing C# since it was invented, I find all of the new features to be implemented in a very coherent, even respectful way. It can seem like things are arbitrarily tacked on, but once you start using the features in earnest, it becomes quite clear that they are very well designed, very well thought out. I have much, much respect for the C# language committee. They've done amazing work in adding features and keeping the language clean and forwards compatible!
- oaiey 8y agoWell said. I am also on that journey and compared to C++ the complexity is still very low and readability very high. I think the only C# feature I have not cracked yet is co/contravariance. And that is more due to the fact, that you rarely need to declare it.
- mattnewport 8y agoI generally like the new features of C# but I still find C++ to be a much more coherently designed language. C# can't escape its poisoned roots in Java despite its best efforts.
- mattnewport 8y agoI tend to think F# is what C# would look like if designed from scratch these days. My take is that C# started out not very good because it copied a not very good language (Java) but they improved on Java a bit and have continued to add things that tend to improve the language within the constraints of remaining backward compatible. Choosing just on the language I wouldn't choose C# but when working in an ecosystem built around C# (like Unity for me currently) I appreciate many of the features as making things a bit nicer.
- oaiey 8y agoI disagree. First of all, F# is functional and therefore has another design. C# also intentional followed C style languages to pick up developers. C# was built for the masses while functional programming is for the elites (F# is has 10k devs using it, VB.NET has 100k and C# more than a million active users). I agree that there are some syntax they would have designed differently when they would have understand the functional programming influence they have today a bit earlier. When they would design it today from scratch, I think they would look at JavaScript.
- mattnewport 8y agoC# comes from a statically typed language tradition (the dynamic keyword aside) and has very little in common with Javascript beyond the surface level of curly braces. The trend in C# features seems very much influenced by functional programming, adapted to be more familiar and palatable to non functional programmers. Some features of C# that seem influenced by functional programming in general or F# in particular include lambdas, type inference, Enumerable (F# pipelines), pattern matching, tuples, null coalescing (a Maybe if you squint), expression bodied members, record types and Range expressions. There is also what looks like an 'aesthetic' influence of F# in language features that reduce ceremony and make for more concise code like auto properties as well as a functional inspired recommended practice of making immutable types.
- oaiey 8y agoFair assessment and I agree. My comment regards JavaScript was more about to pick up existing developers. C# was always meant for productivity. A rapid learning curve (as opposed to learning functional programming first) is very important here.
- rjf72 8y agoOut of curiosity, what do you find poorly designed about them? In my opinion many of these things are elegant to the point of self documentation. Switch case pattern matching in particular is just great: void foo() { object pi = 3.14d; switch (pi) { case int i: Console.WriteLine("pi is an int of value " + i); break; case float f: Console.WriteLine("pi is a float of value " + f); break; case double d: Console.WriteLine("pi is a double of value " + d); break; default: Console.WriteLine("pi is of an unknown type."); break; } } Elegance is of course in the eye of the beholder, so what would you propose as an improvement to something like this?
- vermorel 8y agoWell, C# 8 will be having something much nicer syntax actually: void foo() { object pi = 3.14d; Console.WriteLine(pi switch { int i => "pi is an int of value " + i, float f => "pi is a float of value " + f, double d => "pi is a double of value " + d, _ => "pi is of an unknown type."; }); } Check-out https://neelbhatt.com/2018/05/19/c-8-0-expected-features-part-iii-switch-statments/ https://neelbhatt.com/2018/05/19/c-8-0-expected-features-par... Cheers!
- pjmlp 8y agoLike any product or human languages, programming languages either evolve to make it easier to deal with the complexity of the problems their users need to tackle, or they fade away. Every programming language that advertises itself as simple, it isn't any longer simple if it actually is adopted by the market at large. Go is the newest example, already with a few quirks, needed to revamp their dependecy story, the team finally cave in to look into better solutions for error handling and generic programming.
- hombre_fatal 8y ago> Go is the newest example, already with a few quirks Here's a fun game for people who use Go: why does this error? https://play.golang.org/p/6LTbtuocu5- https://play.golang.org/p/6LTbtuocu5-
- coolreader18 8y agoC natural? (I tried to type U+266E but it didn't save)
- klodolph 8y agoWhy not C-flat? We already have a C--, to pair with C++. https://www.microsoft.com/en-us/research/wp-content/uploads/1998/01/pal-manual.pdf https://www.microsoft.com/en-us/research/wp-content/uploads/... (Note to the pedants: Although C-flat and B are enharmonically equivalent they are not the same.)
- SmellyGeekBoy 8y agoI'd just like to throw C#m into the mix!
- hornetblack 8y agoYou can set the language version and go back to pre linq, pre async days.
- stcredzero 8y agoFirst follow Great Scott's instructions for making an Arduino capacitative switch light. https://www.youtube.com/watch?v=mWR9Q_pTagw https://www.youtube.com/watch?v=mWR9Q_pTagw Then enable it to be integrated with C# code: https://www.allaboutcircuits.com/technical-articles/csharp-windows-application-for-arduino/ https://www.allaboutcircuits.com/technical-articles/csharp-w... Voila! A C# light!
- nicoburns 8y agoReally? C# seems very balanced and coherent to me. What seems messy to you?
- klodolph 8y agoThe messy part of C# is the way it does function overloading. (Speaking as someone who likes C#.)
- lsadam0 8y agoWhy do you think function overloading is messy?
- klodolph 8y agoI’m not making any claims about function overloading in general. Only C#. It interacts in surprisingly complicated ways with a surprising number of other parts of the language. It causes problems with prospective new features. IIRC there have been a number of bugs in the spec and in the compiler wrt overloading. Given the overall high level of expertise of people working on the spec I would say it’s a bit of a minefield, compared to the rest of the language. Edit: Found a Q&A by John Skeet, https://youtu.be/8UvTdobOiJk?t=1292 https://youtu.be/8UvTdobOiJk?t=1292 “Overload resolution is the nexus that every feature ends up contributing to. […] All of these things make overload resolution and type interface really really really complex and it’s always a bit of the C# specification that I found hardest to understand […] and it turns out so did the spec designers, because they got it wrong, and we’ve been trying to fix it.”
- OskarS 8y agoIt might be tricky from a standards point of view, but as a user of the language, I don't think I've ever had an issue with how it handles overload resolution. Every once in a while you get an "call is ambiguous" compiler error, but I've always found the "ambiguity" to be reasonable and easy enough to fix (by making generics explicit or using an explicit cast). It's also a very minor bummer that you can't overload on return type, but that is A LOT to ask of a language. Generally speaking, I've found C# to be a very pleasant language to work in, by far my favorite in that particular "lane" of computer languages (i.e. annoyingly object-oriented enterprise languages, like C#, Java or C++).
- deleted 8y ago[deleted]
- tonyedgecombe 8y agoI sort of agree, it's OK if you have a history with it because all these features are incremental. However if you are a beginner then it could look overwhelming. JavaScript has the same problem but is much worse. It seems we don't know when to stop with this stuff.
- grndn 8y agoC# Light (from 2014) - https://www.slideshare.net/ScottWlaschin/c-light https://www.slideshare.net/ScottWlaschin/c-light
- monocasa 8y agoIs there a formal grammar? You could probably plug that into a SAT solver and not have to guess.
- jonathanyc 8y agoHow would you use a SAT solver? Do you have a cleverer way than generating an expression in a breadth first search?
- hekul 8y ago> fit into a single C# expression Mostly unrelated - but this reminds me of a project I did years ago to put an entire raytracer in a single C# LINQ expression. https://github.com/lukehoban/LINQ-raytracer/blob/master/README.md https://github.com/lukehoban/LINQ-raytracer/blob/master/READ...
- ccvannorman 8y agoThis is awesome! I have been warned that LINQ is often inefficient compared to raw C# with the same functionality -- but I have my doubts as the MS C#/.net put quite a lot into LINQ efficiency under the hood. Did you test this against any raytracing setups that don't use LINQ?
- Analemma_ 8y agoThe thing about Linq efficiency is that under the hood it’s setting up a state machine with enumerators and continuations to be fully general. It’s about as fully-optimized as it could possibly be given the design constraints, but it will always be less efficient than just iterating over a collection directly. But the worst performance hits in Linq come when people don’t know how to use it. The biggest culprit is unnecessary materializations, e.g. ToList() or anything that requires traversing the entire enumerator like All(), so if you’re careful to avoid the obvious pitfalls you should be fine.
- jameshart 8y agoThat's... not really true of LINQ, specifically. I have seen this assumption from developers before, with regard to LINQ-to-objects, that 'the compiler does clever stuff to optimize LINQ expressions', but... no, not really. It compiles a series of method calls. The JIT might do some optimization. But it's not a query optimizer - it just does what you tell it, and underneath it all there's nothing but an enumerator.MoveNext() getting called repeatedly. Setting up state machines and continuations is something C# does when you write generators (with yield) and async methods (with await), but LINQ doesn't do that, particularly. LINQ the language feature in particular doesn't do any of that sort of thing. As a language feature, LINQ is just the 'query comprehension' syntax, which is the 'from x in y where bar select z' coding form. And from the language's point of view that is just a different way of writing y.Where(x => bar).Select(x => z), which it will then try to compile. If it happens to wind up calling generator methods which implement state machines and continuations, or just simple methods that return ordinary objects, LINQ doesn't care, so long as the types all line up. The only other thing that confuses matters with LINQ is that those lambdas it wants to compile can match method signatures that have delegate types, or Expression<> types, in which case the lambda doesn't get compiled as executable code, but rather as an expression tree literal, which is how Linq to SQL and friends were built (but again, that's not a 'LINQ' feature - you can assign or pass a lambda as an Expression<> yourself without involving 'LINQ'). And THAT is where LINQ gets a lot of its worst reputation, because that turns out to introduce a ton of complexity and failure modes that aren't the fault of the C# language, except in as much as it made it possible for a library to get involved in interpreting your code's syntax tree at runtime, which may have been a mistake.
- bluepnume 8y agoI got to 14 (now 15!) in javascript so far: async function* foo() { return yield delete void await null in this instanceof typeof new class extends async function () {} {} } (not sure whether the code needs to successfully run? But it at least parses) What's really fun is seeing how babel transpiled the above function: https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=IYZwngdgxgBAZgV2gFwJYHsICp7vQCgEoYBvAKBkqsoCcBTZBGiGMVOgGwBMYvOG6MYAHdgqZDAgIOHGKhbIAFqhByIIZMGh10cGMjAAHHXoh1hMKB1Cq6AD2R0IXVaEixEKDCyKkAvv4UVH5AA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=false&presets=es2015%2Ces2016%2Ces2017%2Creact%2Cstage-0%2Cstage-1%2Cstage-2%2Cstage-3%2Ces2015-loose&prettier=false&targets=&version=6.26.0&envVersion= https://babeljs.io/repl/#?babili=false&browsers=&build=&buil... EDIT: here's an explanation https://medium.com/@bluepnume/how-many-keywords-can-you-place-in-a-row-in-javascript-fa9046b4c1ff https://medium.com/@bluepnume/how-many-keywords-can-you-plac...
- goatlover 8y agoIt runs successfully in Chrome returning: foo {<suspended>} That's the craziest statement I've ever seen!
- jastanton 8y agoThat's because a generator is being returned from `foo` (the yield syntax gives you this). Try calling next() on the return value of foo. I get: Promise {<rejected>: TypeError: Class extends value async function () {} is not a constructor or null at foo
- deathanatos 8y agoWell, you are correct that that parses. But how? I'm with it right up to around "new class extends"; `new` I'm okay with, but doesn't `new` require a symbol (the name of the type to allocate) after it? I tried parsing "class extends async function" as an expression that boiled down to a type that we could new, but I get lost at "class extends" — don't we need a class name there? (Also, I didn't think `class` could be used in an expression…)
- deleted 8y ago[deleted]
- xtreak29 8y agoNot with keywords but this reminded me of Black Perl : https://en.m.wikipedia.org/wiki/Black_Perl https://en.m.wikipedia.org/wiki/Black_Perl
- userbinator 8y agoAs someone who briefly used C# back in the days of .NET 2.0 (and found it was not much different from Java at the time), it's surprising how much has been added to the language, and my main question is thus (assuming this is a valid expression) "what does it mean?" Especially the non-constant in a switch case.
- mattnewport 8y agoC# 7 introduced pattern matching in a number of contexts, including switch statements. It's not as elegant or powerful as F# or some other functional languages but still quite useful.
- christofosho 8y agoGiving Python a try was fun~ >>> not None and True or [False for _ in dict() if print(lambda: int is str)]
- deathanatos 8y agoBut [] isn't really a keyword, and `dict`, `print`, `int` and `str` (and `_`) are definitely not keywords (they're just function/type names/symbols; they can be shadowed, for example).
- re 8y ago"How many keywords can I fit into a single expression" and "How many 'blue words' can I put consecutively with only spaces between them" (as described in the post) are two pretty different challenges. With only distinct keywords only separated by spaces, best I could do was 10: def _(): yield from None if not True is False else lambda :_
- uryga 8y agoup to 11! (parses on 3.7.2) async def _(): async with await None if not True is False else lambda: _: pass it's a shame that Python has so few expression keywords... `yield` can be used as an expression, but it seems to need parentheses :(
- miguelrochefort 8y agoI can do 14 using parenthesis: case default(string) when await unchecked(nameof(value) as dynamic) is checked(sizeof(decimal)):
- lostmsu 8y agoHe did not even try too hard. You can, obviously, add all of the standard type names and both true and false to your expression easily via ||. Add an out and ref too. Also, if you put a local function into an async lambda (not sure if that's possible), you can add all the control flow operators, and using too. The only things you won't be able to fit there, IMHO, are type keywords (class, interface, etc), namespace, visibility modifiers, static, abstract, virtual, override and operator. Actually, it might be easier to tell which keywords you can't fit into an expression.
- jameshart 8y agoThe challenge the post sets is to line up as many keywords as possible in sequence, without separating commas, semicolons, parens, braces, symbolic operators, etc. So yes, of course if you ignore the constraints on the problem it becomes much easier.
- miguelrochefort 8y agoThe challenge description has an example using parentheses, and doesn't explicitly prohibit them: > yield return sizeof(...)
- Dylan16807 8y agoThe part submitted for scoring is just "yield return sizeof", that's why he says it scores 3. The keywords can be surrounded with more code to make them compile, but none of it counts for the puzzle.
- oaiey 8y agoI agree. I wonder whether he consider an "expression" up to the place where another expression can be placed e.g. && combines two expressions into a third one.
- ygra 8y agoBy my count that's only six keywords as case is a keyword, but not part of an expression.
- magnat 8y agoHere's one with 8 keywords: abstract class A { internal protected abstract ref int X(); } class B : A { unsafe extern internal protected override sealed ref int X(); }
- lalaithion 8y agoHaskell: case x of _ -> True is a valid expression of type boolean for all values of `x`. if b then True else False is a valid expression if `b` is a boolean. These can be nested recursively infinitely, but that's not very interesting, so let's move on. let x = z in y this is another valid expression for any y and z values. do e this is a valid expression if e is a type that implements the Monad typeclass. Basically every other keyword is for type declarations or module importing, and those both aren't expressions, and seem much harder. So, my final expression is: let x = x in do case if let x = x in True then False else False of _ -> return False where `in do case if let` is my longest expression.
- lelf 8y ago> do e > this is a valid expression if e is a type that implements the Monad typeclass. That is not necessary, do do do ()
- victorNicollet 8y agoGiven the rule: expr := 'if' expr 'then' expr 'else' expr An expression may start with a sequence 'if if if if ...' of arbitrary length (and will contain that same number of 'then' and 'else' keywords, although not as a sequence).
- FBT 8y agoA simpler variation of the same concept is to use the fact that for every expression `expr`, `do expr` is a valid expression, allowing you to start a sequence with any number of `do`s. But the challenge as I see it is to stack as many distinct keywords in a row. In javascript you can do the same thing with `await`: for every expression `expr`, `await expr` is a valid expression, so you can start with a sequence of any number of `await`s. That's great, but ultimately not a challenge: the real puzzle is in stacking as many unique keywords as possible.
- FBT 8y agoYou can add `infixl` or `infixr` to that: let x = x in do case if let infixr 7 `x`; x = x in True then False else False of _ -> return False And with the RecursiveDo extension, you can throw `mdo` in there: let x = x in mdo do case if let infixr 7 `x`; x = x in True then False else False of _ -> return False That brings us to `in mdo do case if let infixr` as the longest string of consecutive (distinct) keywords that I can come up with in Haskell.
- deleted 8y ago[deleted]