Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Calling LINQ a "feature" still feels very off to me. It's a set of library functions with some very strange and, at least in my neck of the woods strongly discouraged, SQL-ish syntax that you can use if you like making it harder to understand what your code is actually doing. The library functions are...fine, to be clear, aside from the inconsistent-for-the-sake-of-inconsistent naming (and I know they want to ape SQL, but 'Select' is a map function, sorry). But...they're library functions. You should expect that stuff to exist in a programming language worth a fart.

C#'s feature changes over the last few years have been marginal. They nibble at the edges while stuff like record classes--an actually and materially valuable thing that significantly improves the writing of immutable and functional code--have been punted for multiple versions now.

C# is Java now, in a way it wasn't when it was younger. It's afraid of change and in being afraid of change has slowed to a crawl. Such is probably the way of most programming languages and ecosystems. But it is a little sad for me to see.



LINQ is pretty basic if you are just using the IEnumerable overloads. It get's more interesting when you use the IQueryable versions of things. LINQ to SQL and Entry Framework provide IQueryable implementations that turn C# code in to SQL. This code snippet demonstrates the C# feature these frameworks relies on:

    using System;
    using System.Linq.Expressions;
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int, int>> expr = (a, b) => a + b;
            Console.WriteLine(expr);
            Func<int, int, int> fun = expr.Compile();
            Console.WriteLine(fun(1, 2));
        }
    }
When run, this outputs:

    (a, b) => (a + b)
    3
When a Expression<T> variable is assigned to or a method taking an Expression<T> parameter, the C# code is not translated into MSIL (normal C# is compiled to MSIL). Rather it is translated into an AST. In my example I just compiled it to MSIL, however LINQ-to-SQL and Entity Framework translate the AST to SQL. This macro-like capability is the true power of LINQ. The rest is syntactic sugar (though very delicious sugar).

If I were to try to critique LINQ, it would the normal ORM complaints. Sometimes you have to care about what is going on underneath in the C#-to-SQL translation. Your code may compile, but at runtime the generated SQL might have terrible performance.


You can call it a library, but for LINQ to work at least four major features had to be added to the language or runtime:

-anonymous types -implicitly typed variables -object initializers -lambda expressions

It's interesting that all of these features are very widely used (except maybe for anonymous types) outside of LINQ, but LINQ was the driving force for adding them to the language.

Anonymous types were needed to do the equivalent of "select field1, field2" that are familiar to SQL programmers, or to have intermediate values in a chain of Select/Where/Select operators. Implicitly typed variables (var) were needed to refer to the above types. Object initializers are needed so you could create the anonymous types, and non-anynymous ones too without breaking the LINQ chain. Lambda expressions of course were needed. All of the above are used outside of LINQ.

This was done in 2007. Java got streams and lambda expressions in 2014. JavaScript got it in 2015.

The SQL like syntax - well it exists. I think many people don't realize it's there. Everyone uses the method/lambda syntax.


All true! But, as you note, they're prerequisites for LINQ, not LINQ. Thing is, though? These days, I would say that all four of the features you describe are barely table stakes. Having lambdas made C# cool in 2007, but it's not 2007 anymore and that the four features you listed are probably the important new features I can think of since 2007 is why I like C# a lot less today.

As is probably obvious from my reference to them, I believe that record types would be a contender for "most important new feature"--even moreso than async/await--after spending a lot of time with Kotlin and data classes, it is nearly impossible to go back. C# provides you with the choice of manually-updated boilerplate or mutable objects through your whole stack. That's what has generally kept me away from C# since exposure to first Scala, later Kotlin, and most recently TypeScript. And, while I now work at a C#-first company (not on the C# backend), I don't see a real reason to change that for me personally until they make writing correct code easier.

In many ways, TypeScript seems to me to be a wiser future than what C# is presenting, but that's a whole other kettle of fish.


> Having lambdas made C# cool in 2007, but it's not 2007 anymore and that the four features you listed are probably the important new features I can think of since 2007 is why I like C# a lot less today.

It's not lambdas but expression trees that are the key feature that made LINQ possible. That is pretty unique feature among programming languages. So you might want to reevaluate your opinion about "table stakes features".


Seeing as how every use of expression trees I've ever seen in C# is something like "try in vain to make it so people don't have to understand SQL", perhaps you will forgive me for not being particularly impressed by it.

I view C#'s expression tree stuff as a neat experiment. It's cool. But it doesn't and shouldn't change one's programming life.


Perhaps that's just what you've seen then and not really representative? For example the Roslyn compiler service written in C# uses expression trees to do what it does, and it opens up an entire field of possibilities for meta programming in C#.

> But it doesn't and shouldn't change one's programming life.

What's an example of something that would?


Nice moving the goalpost there (table stakes -> changing one's programming life).


I uh. I don't think any goalposts have moved? When introduced, stuff like lambdas and the libraries that could build on them did do exactly that. But they're table stakes today, in 2018. Where's the movement?


Expression trees, as they are in C#, have never become table stakes and remain pretty unique and advanced language feature. When I and several other commenters pointed that out, you sad something like "yeah, but they don't exactly change programmers life". That's called moving the goalpost.


I mean, if you choose to ungenerously parse it as "table stakes" being the important part of the comment instead of, yanno, that these things that are table stakes are the thing that people actually care about in that whole ball of features, sure?

Hermes Conrad is not aspirational, my guy.


It's not lambda expressions necessary for LINQ but the ability to access the expression tree and translate C# code into other languages (like SQL). There are not many languages with that feature.


I agree with everything you said, except "JavaScript got it in 2015". JavaScript had first-class functions since its inception in 1995 before any of Java or C#, and methods like `array.map()`/`filter()` etc since ES5.1 in 2011, which were already widely used long before the `=>` syntactic sugar was added in 2015.


But those functions only worked on arrays or maybe "array-likes" if you're lucky. Notably, they do not work on lazy enumerables, like LINQ does. And then linq goes farther, and allows them to used on queryable data sources that aren't even enumerable.


JavaScripts stdlib is smaller, C# has a huge stdlib. map/filter on Iterables is just an npm install away, e.g. https://www.npmjs.com/package/iterare


When I mean LINQ, i don't necessarily mean querying SQL. Working, filtering, grouping, selecting from collections in memory works very well. And i don't necessariliy also mean LINQ query syntax, but it can also be method syntax: .Where().Select()...

And the power of ExpressionTrees that allows you to interpret code your own way. For example, substituting SharePoint CSOM library

  .IsPropertyAvailable(string)
with

  IsPropertyAvailable<T>(this T clientObject, Expression<Func<T, object>> propertySelector) [0]
is neat. Instead of magic strings you now have compile-time checking. Now you write

  .IsPropertyAvailable(s => s.ServerRelativeUrl)
instead of

  .IsPropertyAvailable("ServerRelativeUrl")
As for SQL, it may sometimes be a blocker, yes. But EF Core, for example, provides a FromSql method to use for complex queries.

[0]: https://github.com/SharePoint/PnP-Sites-Core/blob/206ee97d4b...


LINQ is not just library functions. It stands for "language integrated query" and is a full data modeling and querying system designed around lambdas and expression trees.

It can be easily translated to other query languages (like SQL) or functional paradigms, and is much more advanced than some map/iterate operators on generic types (which languages like Go dont even have).

The LINQ query syntax (from x in list select x.a) is syntactic just sugar to easily write complex expressions that the compiler turns into the same standard list.Select(x => x.a) method calls.


I promise you that I understand the idea; I have run in .NET circles, particularly Mono, for a long time. It is an interesting goal, but I prefer to stay somewhat in the land of what is--and so I am talking about what is actually done with it in the wild, not what architects blog about. It's map-reduce-fold in the majority case, and when it's not (getting into the IQueryable minefield) I often have found myself going "oh, no."


The weird special-case LINQ syntax appears to have receded and I don't recall seeing it in the wild for some time. Hopefully we can just all agree that was a bad idea and it can get officially deprecated and go away in future versions.


Yeah, I mostly use the methods to do LINQ and rarely use the linq syntax -- but -- it's actually quite powerful and if you have to do a reasonably complex query it's much easier to compose and read in that syntax than the alternative mess of method calls and lambdas.


Umm, the special-case LINQ syntax actually it is nothing special on itself, it just calls those methods anyway. You can actually extend LINQ by providing your own Extension methods for IEnumerable or IQueryable


Right. Which is why it's jarring seeing these blocks of non-idiomatic almost-SQL DSL embedded in otherwise consistent C#. Particularly when you have to materialize a query by calling ToList() or similar - you end up with an extra unsightly mix of query and method syntax with the query wrapped in parentheses. Or else cluttered up with defining intermediate variables that need to be named.

Much better in my opinion to just stick with the fluent method chaining style of using LINQ methods.


You'll never get me to agree. I still use it when I need a `let`.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: