What?? Visual Studio 2013 just shipped a week ago. They have released 5 versions of the MVC framework in the last 3 years or so. The new async/await stuff is fantastic. EF6 is new. SignalR is new. We get new Windows Azure SDKs every 3 months or so. They are constantly improving everything.
None of that, except async, is a C# feature. And that was taken from a 6-year-old F# feature of the same name, except in F#, it was implemented as a library, not baked into the compiler.
MS is moving .NET ahead as a platform, but C# doesn't seem to be doing much. As another comment said, they're rewriting the compiler and then are supposed to make it take off again. Still, 5 years with minimal additions seems "stagnant" to me.
> it was implemented as a library, not baked into the compiler.
You keep saying this as if its a bad thing. Personally I consider it the exact opposite. I have no experience with F# but my experience with features that are baked into the language vs in a library is that having a feature be a first class citizen gives all sorts of benefits in terms of usability, consistency, tooling support, not to mention being able to justify introducing the fancy new feature into your project.
Nothing about having it be a library means there's anything less in terms of usability, consistency, or tool support. It's not like people avoid System.Text.RegularExpressions because it doesn't have a compiler-intrinsic ~= match operator.
The exception I see is if a certain pattern requires a drastically different compilation method, even if only as an implementation detail. That would perhaps break debugging support. But that's not much of an argument, because nothing stops you from special-casing your tooling support (which you'd presumably do anyways).
It just seems incredibly inelegant to have to special-case things in a language when the general case could be implemented.
case /\(.+)!(.+)@(.+)/
nick, user, host = $1, $2, $3
...
vs
if( (match=new Regex("(.+)!(.+)@(.+)").Match(caseInput)).Success )
{
var nick = match.Groups[1].Value;
var user = match.Groups[2].Value;
var host = match.Groups[3].Value;
...
}
If I'm using a ton of regexps, having domain specific language support can certainly help keep things simpler and clearer, aiding usability by aiding readability. This is important enough people can and will resort to implementing their own domain specific languages. While I'm all for erring on the side of general purpose language features for a general purpose language, if only to reduce the possible explosion of corner cases for language design mistakes, discounting language level support as "inelegant" and having no benefits is, to me, tantamount to discounting DSLs in general for the same.
And perhaps you do so, but the argument doesn't resonate with me.
F# active patterns allow you to do exactly this kind of thing, in a general and extensive way, without building support for regex into the compiler. That's what I'm talking about.
And it takes all of 4 lines to add the support:
let (|Regex|_|) pattern input =
let m = Regex.Match(input, pattern)
if m.Success then Some(List.tail [ for g in m.Groups -> g.Value ])
else None
Boom, done. I'd call that a far more elegant approach than hardcoding regex into the language. Edit: And nothing is stopping the core developers from including such patterns in the standard library. So you get the best of both worlds, a common well-known manner, and a useful general purpose feature that people can extend and use if they need so.
Usage looks like:
match phone with
| Regex @"\(([0-9]{3})\)[-. ]?([0-9]{3})[-. ]?([0-9]{4})" [ area; prefix; suffix ] -> ...
If you look at how much has been added to say, python or ruby or java in the same time frame it seems comparable. .NET 4 (released in 2010, so really only 3 years ago) added a ton of new features (dynamic, better generics, better expression trees, optional paramters, etc.).
NET 4.5 was comparatively a more quiet, but I think that's just a consequence of the language maturing.
C# 4 did bring forward the covariance/contravariance feature that's been in the CLR since v2, yes. And they did add dynamic, which was the big major feature of v4.
Better expression trees, as I understand, aren't accessible from C#. That is, the C# compiler doesn't allow you to write arbitrary code as an expression. What shipped was the DLR-based expression trees, which are more flexible, but require a cumbersome API. So that's not really a feature. (Correct me if I'm wrong; I haven't used C# extensively since 3.0 as I moved to F# for .NET tasks.)
Optional parameters were a mistake to leave out of the original spec. MS insisted that providing a ton of method overloads was a more elegant approach, for some insane reason. And at any rate, the C# implementation C# is the same, lame, VB/C-style callsite embedded optional parameters, which hampers their usefulness (but it's still much better than overloads!).
Perhaps I have unrealistic expectations from C#, as I certainly don't appreciate the effort required to design a language for such widespread use.