In comparison, C# adds special compiler keywords for one specific example, just like they did with LINQ. That seems rather ugly IMO. Providing building blocks and letting libraries fill things in is a lot nicer.
This is more of a "C#'s finally catching up with basic features".
The correct characterization is to view this as a pipeline from a research language, to a specialists' language, to a common man's language.
Six years isn't really all that long to wait for a specialist feature to be 1) motivated 2) conceived 3) prototyped 4) validated 5) justified 6) implemented 7) tooled 8) released 9) marketed. Given that there are hundreds of ideas and only so much time, the "minus 100 points rule" [1] basically means that it's no easy feat for a feature like this to show up in a mainstream language. When you consider the quality bar, level of IDE integration, the magnitude of the education effort, and all the other odds and ends, it's something of a minor miracle.
[1]: http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx
People would be upset if C# didn't have for loops; why aren't they upset the type inference is nearly useless?
Can you honestly not comprehend the answer to this? There are plenty of languages without type inference and shit gets done fine. People don't rely on it. People do rely on for loops.
Where can I see an example of useful type inference?
I did a pretty fair line-by-line translation of a C# app to F#, and the F# version needed 1/20th the number of type annotations.
Because the existing type inference is good enough for the average enterprise programmer.
You know, those guys with good enough CS grades, that just do what they are told and don't even know HN exists.
C# 3 added LINQ, which is a major breakthrough for end-users, although I'm not fond of the query language. So really, C# 3 just added in some basic features you expect from proper languages. I do understand this required a huge amount of work, esp. with the tooling required.
C# 4 added dynamic (F# provides ? operator you can provide your own implementation for, if you really feel that strings look ugly). Oh, and it finally backpedalled on the no optional parameters (although the optional parameters is the same broken C-style callsite implementation).
C# 5 added async (F# had a more flexible implementation 6 years before).
What else? C# seems to have stagnated, although I understand that's a feature for some of their users. C# still lacks type inference in most places, making it extra verbose. C# expression trees are still very limited. C# still can't easily do tuples. Not sure they deserve a break; this was MS's "flagship" language.
OTOH, The CLR itself doesn't seem to be getting any upgrades, either - IL stayed locked at v2. It's as if they realised they have done better than the JVM can ever do (generics via erasure just sucks) so why bother pushing it further?
- Improve the GC algorithms, most JVMs have better GC algorithms
- Improve the code quality of the JIT and NGEN compilers, specially the set of applied optimizations
- Expose auto-vectorization and vector instructions (similar to Mono.SIMD)
- Expose something like C++ AMP on .NET.
Some of this was slightly improved on 4.5, but they could do more.
TO me that seems to be a good thing. The language has picked up a lot of great features that make it nice to program in, but it is also making the language huge. How do all these new features interact? What are the emergent properties of the language? Personally I could use a few years to A) get legacy code caught up B) Explore what already exists.
Like C#, core.async uses a lexical compiler transform to produce a finite state machine for the co-routine. Unlike C#, Clojure can achieve this with a user-level macro, instead of a compiler change. Both C# and core.async differ from Go, in that Go's coroutines have dynamic extend, by virtue of being heap-allocated stacks with a custom scheduler. In practice, this has a minor impact on higher-order usage of co-routines, but is a smaller problem than you'd think, it's generally advisable to minimize higher order usage of side effects.
Both C# and Go's approaches can be implemented as Monads, yes. However, Monads are a significantly more abstract thing than either CSP or C#-style Tasks. The do-notation is barely concealed continuation-passing style, which is generally less pleasant to work with than traditional imperative constructs for side effects such as send & receive. "More powerful" isn't a really useful measurement for practical use.
AsynchronousBureaucraticProccessDispatcherFactoryFactoryFactoryInterfaceProvider
There I fixed it for you. And of course you have to specify the provider in the META-INF/async file.