How many keywords I can fit into a single C# expression?
tabsoverspaces.com
tabsoverspaces.com
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=&buil...
EDIT: here's an explanation https://medium.com/@bluepnume/how-many-keywords-can-you-plac...
return (
yield (
delete (
void (
await (
(null) in (
(this) instanceof (
typeof (
new (
class extends (
async function () {}
) {}
)()
)
)
)
)
)
)
)
)
`class extends X {}` is an anonymous class extending X. Syntactically, X is an arbitrary expression, which `async function () {}` is. At runtime, X must be a constructor on pain of TypeError, and an async function isn’t; this could be considered grounds for disqualification, as under no circumstances will it execute without exceptions.`new` does not require an identifier or path to follow it: it can be an expression. So `new (foo)` is fine. It’s just kinda greedy as regards an open paren of a function call: `new foo.bar(baz)(quux)` is equivalent to `(new (foo.bar)(baz))(quux)` rather than any alternative grouping. Remember also that `new foo` is legal, and equivalent to `new foo()`.
In Chrome:
> (new class {}).__proto__
{constructor: ƒ} return yield delete void await(
(null in this) instanceof typeof(
new (class /*anon*/ extends (async function () {}) {})
)
)
Maybe? Is `await foo in bar` a special case in javascript?[1] https://www.ecma-international.org/ecma-262/9.0/index.html#p...
[2] as in `for (foo of bar) {}`
[3] as in `class { static foo() {} }`
[4] as in `import {foo as bar} from 'baz'`
[5] as in `{get foo() {}, set foo() {}}`
foo {<suspended>}
That's the craziest statement I've ever seen!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 fooI was going for unique keywords with no repetitions.
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/READ...
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?
It does have overhead, but it shouldn't be avoided for that reason unless you're really trying to squeeze performance gains out of your code. Eric Lippert, the designer of LINQ, has this to say [1].
[1] https://stackoverflow.com/questions/3769989/should-linq-be-a...
var a = new double[10000];
var b = a.Select(x=>x*x).ToArray();
I'd expect LINQ to create a List or a similar structure when executing the select, then copy the output to the final array. If you were operating on two arrays directly, I would expect less memory allocations (unless these things get optimized away).
The CLR does do some type checking to try to propagate the size if it can, but that's by no means guaranteed.
Edit: In skimming the source available on sourceof.net, it looks to me like the array doesn't actually get propagated far enough, so the construct in GP will in fact incur several unnecessary array copies.
You can see the `ToArray` implementation at [0], which defers the implementation to [1]. The implementation checks for ICollection to get the exact size, but the type [2] that `Select` returns doesn't implement ICollection, so `ToArray` has to fall back to the less efficient algorithm.
[0] https://referencesource.microsoft.com/#System.Core/System/Li...
[1] https://referencesource.microsoft.com/#System.Core/System/Li...
[2] https://referencesource.microsoft.com/#System.Core/System/Li...
Was trying to open your links but I am served an expired tls certificate from Microsoft!
[1] https://en.wikipedia.org/wiki/Erik_Meijer_(computer_scientis...
As an example, a .Where(...) on a List<...> object is exactly what it claims to be: a linear pass through the list. Sometimes that's exactly what you need. Other times, it's more elegant and fast enough to represent an operation as a list comprehension. Finally, sometimes you really do need to eke out more performance. It's only the last case where LINQ is a bad idea.
Yes, but it uses deferred execution, which means that it returns enough information to perform the filter, it doesn't really do much until you start to enumerate the list. If, for instance, you continue to do a Take(5) on the result, the filter is only applied until it finds 5 elements that satisfy the filter (or until there are no more elements).
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.
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.
abstract class A
{
internal protected abstract ref int X();
}
class B : A
{
unsafe extern internal protected override sealed ref int X();
} 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. 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. 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).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.
That is not necessary,
do do do ()Not every string of tokens is an expression.
case default(string) when await unchecked(nameof(value) as dynamic) is checked(sizeof(decimal)):`case null when await this is false is true:`
Actually, unless you count only distinct keywords, this can be extended indefinitely.
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.
So yes, of course if you ignore the constraints on the problem it becomes much easier.
> yield return sizeof(...)
>>> not None and True or [False for _ in dict() if print(lambda: int is str)]
def _():
yield from None if not True is False else lambda :_ 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 :(https://www.youtube.com/watch?v=mWR9Q_pTagw
Then enable it to be integrated with C# code:
https://www.allaboutcircuits.com/technical-articles/csharp-w...
Voila! A C# light!
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
“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.”
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++).
JavaScript has the same problem but is much worse.
It seems we don't know when to stop with this stuff.
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.)
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.
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.
When they would design it today from scratch, I think they would look at JavaScript.
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.
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.
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? 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-par... Cheers!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.
Here's a fun game for people who use Go: why does this error?