LInQer – C# Integrated Queries ported to Javascript
github.com
github.com
Pretty neat project. LINQ is a great time saver in .NET development, I'm looking forward to messing around with it in javascript.
Off the top of my head, the only possibility I see is allocating objects with `Select` or the like. If one can avoid that, the only overhead should be the predicate/selector method calls...?
Language integrated query comprehension is also duck-typed, so if you explicitly implemented Select and Where and friends to all return appropriately crafted structs, you could write full LINQ queries and enumerate them without heap allocations.
But you’re right that to use LINQ to objects you must have an IEnumerable because you rely on the type to bring in the extension methods.
Be interesting to imagine what c# language features would be needed to deliver a stack-allocatable LINQ...
My LinqFaster repo has some benchmarks showing time differences for some common operations in a fairly worst case scenario (where the work done in each iteration is small) https://github.com/jackmott/LinqFaster
It would just be nice if C# reliably compiled this abstraction down to efficient code, the way C++, Rust, and Java Streams do.
Some developers understand this, for other less savvy developers, it's just voodoo. In any case, if the collection is 100 items, there's no real advantage to optimizing.
Also, a foreach loop with an if statement is probably faster than a select statement, because it's not allocating a lambda and going back and forth in the stack; and a for loop indexed by a counter is probably faster than a foreach loop. Those, however, are micro-optimizations that really need their value shown in a profiler.
I once wrote a library that allowed indexing collections by different properties. It was quite intense, and it paid off as the application has lots of RAM-based queries and its internal collections can scale to 20,000 or 250,000 items.
(Unrelated: I wanted to open source the library, but my boss wouldn't let me. Sigh. Maybe I should have pushed back around my annual review that being able to put my name on a useful open-source library is important career development.)
I've long joked that ToList() should be considered harmful, because it's an interesting crutch that gets over used and List<T> is so often the wrong data type for the operations to follow. Every time I have stepped in to performance optimize a Linq-heavy codebase, I've started by finding and removing nearly every use of ToList().
ToLookup() is one of the frequently unsung heroes of Linq that gets overlooked too often. It is the "indexing" operator, as ILookup<K, V> is the out-of-the-box multi-value Dictionary of .NET. I think ToLookup() gets overlooked often because there's no concrete, mutable Lookup<K, V> in the BCL (MultiValueDictionary<K, V> exists in the Experimental Collections NuGet package).
Deleting unnecessary ToList()s (where a query was "cut in half", presumably for debug reasons) and then converting nearly every remaining instance of ToList() to ToDictionary() or ToLookup() based on queries/usage patterns that followed was often alone a huge performance boost to Linq code that I found, simply because it was using a more useful data structure. Sometimes you don't even have to do any of the more complicated performance testing/debugging work and can stop there at the easy stuff.
I've thought for a long time that "coloring" IEnumerable<T> versus IQueryable<T> queries differently, at least at the syntax highlighting level, would go a long way to help people better their mental models of Linq. Especially at the complicated transition places where an IQueryable<T> query "falls back" to IEnumerable<T>.
The problem is that I never had problems! (With LINQ-to-Objects performance.) That’s why I’m asking in the first place. Maybe I intuitively avoided all the pitfalls, who knows. It would be cool if you could share some (abstract) operator chains that caused problems in whatever you’re working on.
https://github.com/jackmott/LinqFaster
While those can look really bad - orders of magnitude slower! Keep in mind with things like Where/Select, its a fixed amount of overhead. If you are larger amounts of work, or IO in those clauses, then the fixed amount of overhead is small in comparison.
There are some common pitfalls you can avoid: Don't use GroupBy - it actually has a lot of overhead. Put things into a dictionary by hand instead. Instead of chaining together multiple Where clauses, just use one Where clause and the && operator. This saves you some overhead and has the same semantics. Consider whether you can presize a collection and then fill it up, rather than calling "ToList()" or "ToDictionary" because those will not presize the collection for you. The list will have to reallocate as it grows etc.
Keep in mind that depending on your .NET version, calls to Count() may have to actually iterate over the whole collection even if that isn't necessary. .NET Core 3 fixes some of those cases, older versions may iterate over an IEnumerable backed by a List just to get the count, when the count is already there.
At work right now, we just operate at very high scale, so in high traffic endpoints its worth it to pull microseconds of time out, and reduce memory use by bytes. Because everything gets multiplied by large numbers of requests.
Thanks for your insight. I also found out .NET Core 3 has pre-sized versions of ˋToDictionaryˋ and possibly others.
However, if you have some slow SQL query or redundant SQL queries, I bet it is performance wise better to do something about that.
In the case of Roslyn, the C# compiler, they avoid Linq on the hot paths (which run many times, for instance when typing in Visual Studio!) but outside hot paths linq is still used.
I hope they update LINQ in .NET to address these use cases.
Being a nonpaid test, I did what was expedient to me that met the requirements. Most of the questions were 1-liners using Linq, but that wasn't acceptable.
I probably didn't make any friends when I pointed out that I solved all of their problems and that there were no other conditions on the solution. Fuck 'em.
Interviewing with hedgefunds is a pain in the ass. Bunch of premadonnas that think their shit doesnt stink (but is probably the rankest WTF code you'll ever see) - and I say this having spent a decade at one.
A separate fund gave me a private hackerrank challenge. My code passed all of the public requirements and examples, but failed a private test case. No info given about the failure. Emailed the recruiter about my frustration with it, said fuck it, I'm not interested. Figured if theyre like that in an interview, theyre probably colossal pricks to actually work with. No surprise, I didnt get an on site interview.
0.1 + 0.1 == 0.2; //= true
0.1 + 0.2 == 0.3; //= false
It's not the language, it's https://floating-point-gui.de
And here I thought that by using F# I would even be protected from this shit but unfortunately it has the same problem... Performance should be opt-in! (favouring correctness over it) :facepalm:
Thanks
https://docs.microsoft.com/en-us/dotnet/api/system.decimal?v...
This means it can rewrite the native language query, and say, turn it into a SQL or graphql query, or a stream filter, or check against cached data or all kinds of clever things.
One of the most mind-blowing moments I can remember in my career is when I realized just how powerful the tandem of LINQ and IQueryable was.
Now, writing your own LINQ provider is no walk in the park, but is a really satisfying project and worth doing once, if only to get a deeper understanding of what's going on with expression trees and the inner workings LINQ. It's almost like a language within a language.
With that power comes the responsibility to actually understand what it is that you are doing, and where the abstraction leaks. If you are not careful, and do not understand the ramifications of what you are writing, it is very possible to write some horrifically inefficient code, or code that looks correct, but will break at runtime.
I have seen many a horror with Entity Framework or Linq-to-SQL when I dug in to fix badly performing code, and hooked up tracing to track the SQL code that is actually generated and executed. Multiple iteration of an IEnumerable is very easy to run into, although static analysis is getting good at flagging that with a warning. Trying to use an IEnumerable that hasn't been materialized from the expression tree into the actual data after the database connection or other resource has been disposed is another common gotcha. Understanding what operations can be translated by the expression tree engine is very important. In LINQ database ORMs, the way that foreign keys behave can vary wildly depending on how the query is written and on how the ORM happens to handle those accesses.
'You're too ignorant to be judging whole ecosystems as "shit".' is also not meant offensively, simply as a statement of fact.
Someone who presumably writes code for a living, but doesn't understand binary floating-point numbers, and finds themselves in a position to criticise JavaScript as being "shit" while not even having a sufficient understanding of their preferred technology to realize that it has the same (expected) behavior is objectively someone who "needs to study more" and who is "too ignorant to judge whole ecosystems as 'shit'".
Edit: Before someone says it's because I wasn't being helpful, both my comments also linked to http://0.30000000000000004.com/ which contains an explanation.
That OP was so wrong about their 'smoking gun of Bad' hopefully makes them pause in the future before condemning something. Though I don't think such low-effort condemnation posts belong on HN anyways.
Statement of fact or not, and insult or not, the question is, does the comment contribute to the conversation? And does it do so in a tone that is likely to be received well by others?
Even if someone is inexperienced, is telling them to study more and telling them that they are ignorant useful? Not really IMO. Aside from the thing about tone, it is also a question of whether it is giving actual actionable advice.
Actionable advice includes pointing to specific sources of information, like one of the sibling comments did.
Non-actionable advice includes telling someone to just study more in general.
Saying that someone who doesn't understand binary floating point numbers is ignorant and needs to study, while pointing them to a study resource, isn't flaming, it's an act of selflessness. It's a favor, a grant of knowledge and a nudge in the right direction.
> Even if someone is inexperienced, is telling them to study more and telling them that they are ignorant useful?
Yes
> Aside from the thing about tone, it is also a question of whether it is giving actual actionable advice.
The sibling comments did not exist when I posted my first one. It had also a link to actionable advice.
Mind you, no-one is forced to know how binary floating point operations work, as long as they don't intend on passing incorrect judgement that's brash and impolite.
All feedback is welcome :)
An alternative approach could be to parse real JS expressions using babel (at runtime) - essentially giving you the same flexibility as C#.
So you could allow users to write:
typedKnex.query(User).where(u => u.id > 100 && u.city === "Calicut")Well, it could convert the "delegates" back to strings, and re-parse an AST from that. Not that I'm volunteering to write it.
Earlier, I wrote a tool called chimpanzee[1] to parse such ASTs into SQL. Not easy (but not too hard either); there are so many different ways to express something in JS. Here's an example (using chimpanzee) which checks if an expression is doing a sort (on an array/table) and to extract sort fields and order. https://github.com/jeswin-unmaintained/isotropy-ast-analyzer...
[1] chimpanzee: https://github.com/jeswin/chimpanzee
- Swift https://github.com/mythz/swift-linq-examples
- Kotlin https://github.com/mythz/kotlin-linq-examples
- Java https://github.com/mythz/java-linq-examples (Java 1.7)
- Clojure https://github.com/mythz/clojure-linq-examples
- Dart https://github.com/mythz/dart-linq-examples
- #Script LISP https://sharpscript.net/linq/restriction-operators?lang=lisp (Interactive, .NET Lisp)
- #Script Code https://sharpscript.net/linq/restriction-operators?lang=code (Interactive, .NET JS-Like)
- Elixir https://github.com/omnibs/elixir-linq-examples
- Python https://github.com/rogerwcpt/python-linq-samples
- Groovy https://gitlab.com/svkj/groovy-linq-samples
Most languages fare well in both verbosity and readability so I don't view LINQ as a major strength of C# anymore, it's just a well designed, typed query language with the USP of being able to capture and traverse an expression's AST which different LINQ providers can take advantage of by translating the intent of the query into a different DSL, most commonly used by ORMs to convert to SQL and execute the typed C# Expression logic on the RDBMS Server.
The second is, if you're going to compare languages based on compactness of syntax, you can't write other languages using a compact fluent syntax, and then write c# with SQL and say the other languages are more compact.
LINQ comes with a compact fluent syntax and a verbose (but more familiar at the time and thus easier ramp) SQL syntax. The documentation when introducing concepts it used the SQL syntax. As anyone who has familiarity with the tools uses the fluent syntax. Yes you can say you used samples from the docs, but those are for teaching. You can't compare them to hand written code using a compact syntax. Use the flutent syntax for all both langs to do an accurate comparison.
I applaud the effort, but it's not an accurate comparison in it's current form.
LINQ is the query language that's used in the 101 LINQ Examples, it's not the "compact fluent syntax" you're referring to of calling IEnumerable<T> extension methods directly, that's not the "Language-Integrated Query" that LINQ stands for, the purpose of Microsoft LINQ Examples was to showcase how to use LINQ.
The purpose of each language comparison was to achieve the same result of each example, but in the idiomatic style of that language.
Yes I both read the msdn examples docs many years ago when they came out when first learning LINQ, and clicked through and read them again in your provided links of your project docs to confirm your implementations.
I thought there were two possible things happening here. I took the charitable interpretation and assumed you may not be familiar with the fluent syntax nor the fact it is the standard syntax. Or possibly you may not be familiar with the fact that those examples are meant mainly for instructing the concepts underlying each function and are not best practices. I had all of that in mind when I wrote the following.
> The documentation when introducing concepts it used the SQL syntax. As anyone who has familiarity with the tools uses the fluent syntax. Yes you can say you used samples from the docs, but those are for teaching. You can't compare them to hand written code using a compact syntax
The other interpretation, the one I avoided was that you were not looking for a fair comparison for programmers to learn from. Your response makes it harder for me to ignore this second interpretation.
If I were the owner of a project and looking for a fair comparison, I'd be happy if someone let me know I wasn't using the correct practices in C# and would want to update my project to reflect that. Possibly creating an implementation in C# of what LINQ actually looks like in use. Instead you're digging in deeper behind the "that's what's in MSDN examples documentation".
To put it directly, if you want a fair comparison between languages for people to learn from, and make judgement from, then make it so. Don't use the antiquated SQL syntax.
Please make this a helpful project for people and use the de facto standard syntax. There are differences between languages and a fair comparison would let people learn them.
You shouldn't need any charitable explanation or imagination to be able to read the title and purpose of each project that's clearly explained in the docs.
Your criticism tantamounts to:
> Your 6-7 year old "C#'s 101 LINQ comparisons" series is using the actual "C#'s 101 LINQ Examples", Unfair!
Then go ahead and produce your own series, instead of wasting your time and energy on criticizing others efforts.
The fact that you appear to be getting emotional and are resorting to personal jabs means it's probably time to end this discussion.
I've tried to to explain it to you, other people in threads on this page have tried to explain it to you, the msdn documents as well themselves explain it. At this point there is nothing left to do. You're going to hold onto the "it's in msdn so it's fair" line of justification. It was good chatting with you.
You're complaining that the projects does what it set out to do, which is your issue, so go ahead and create the series that does what you want - I promise I wont tell you to change it to suit my tastes.
The idiosyncratic special syntax was misguided and fortunately is largely abandoned. Alas we are stuck with the reserved keywords forever.
Also fortunately, it's not totally abandoned. You can have my `let` when you pry it from my cold fingers.
The "idiosyncratic special syntax" you're referring to is called LINQ.
See: https://docs.microsoft.com/en-us/dotnet/csharp/programming-g... http://twistedoakstudios.com/blog/Post2540_optimizing-just-i...
- OrmLite https://github.com/ServiceStack/ServiceStack.OrmLite (C# POCO ORM)
- PocoDynamo https://github.com/ServiceStack/PocoDynamo (C# DynamoDB Client)
Of the features that differentiates it from querying functionality in other languages is its Expression Trees support that I've highlighted in my last paragraph which is essential for being able to implement alternative LINQ providers.
Here is an example: https://github.com/sprache/Sprache#usage
It is rumored that Linq was initially implemented using monads, but that approach was abandoned due to performance reasons.
MyObject<B> SelectMany(MyObject<A>, Func<A, MyObject<B>>)
or perhaps more familiar to the Haskell folks: SelectMany :: M a -> (a -> M b) -> M b
i.e. exactly the 'bind' operator for Monads.I think you also need to implement a 'Select :: M a -> M b' operator, rather than a 'return :: a -> M a' operator, which would be closer to a true Monad, but the 'bind' operator is the one that really matters anyway.
I call it a rumor because I cannot remember where I read it, so it is quite possible I have misunderstood it.
I wrote the Clojure examples 6 years ago as a beginner whilst I was learning Clojure but I thought its built in functions were already well suited for LINQ examples.
Do you have an example where transducers would help with verbosity or readability?
// Not ideal
list.Select(x => ExpensiveFunc(x)).Take(5);
Transducers can be terminated early - https://dev.to/greencoder/build-your-own-transducer-and-impr...For the same reason, if you 'foreach' over the result of a linq query multiple times, it will evaluate the whole sequence again, which often isn't what you want - so people tend to call .ToArray() or .ToList() at the end to fully evaluate it once.
My bad, indeed it does! (Embarassing, my job used to be working on LINQ-based ORM libs. Though more than a decade back.)
http://clojure-doc.org/articles/language/laziness.html#lazy-...
Datascript allows you to write datalog queries in ClojureScript(and JavaScript) that operate on a b+ tree maintained by the library. It includes first class relationships and unique keys.
https://github.com/queryverse/Query.jl/blob/master/README.md
http://www.queryverse.org/Query.jl/stable/linqquerycommands/
Previous HN discussion on LazyLinq, which is another JS implementation - https://news.ycombinator.com/item?id=15913571
I know there are overhead to convert map and set into array but it should be fine for non huge data set.
Some more helpers are useful (first, last, max, take, skip ... all with variation). They should added to the core language.
"ported to javascript for performance"
In what alternative universe are things ported to javascript for performance?
The bigger argument against JavaScript in my view is the way the language is so antithetical to every lesson you'd have thought the industry has learned - even Go is safer than JS and Go is designed like it's from the 70s
The beauty and power of Linq is that it is translated to expression trees at runtime, can be interpreted by a provider, transformed to basically anything - sql, MongoQuery, intermediate language, etc. - and you can pass around types of:
Expression<Func<T,bool>>
to different providers.Like
repo.Find<Customer>(c => c.Age > 50 && c.Sex == “M”);It can't be implemented at runtime, if your method is passed an opaque compiled delegate, it's not possible for the .NET Runtime to deconstruct its pre-compiled AST.
If your method accepts a `Expression<Func<T,bool>>` the C# compiler passes it the AST of the Expression instead of the compiled delegate.
For an example, paste the code below to https://sharplab.io
using System;
public class A {
A() => M<int>(x => x == 1);
public void M<T>(Func<T,bool> expr) {}
}
public class B {
B() => M<int>(x => x == 1);
public void M<T>(System.Linq.Expressions.Expression<Func<T,bool>> expr) {}
}
and look at what the compiler generates for class A vs class B's constructors.You can use Roslyn in code, but that's using the C# compiler in process.
It's compiled into Expression Trees at compile-time (if it's an IQueryable). If it's an IEnumerable, Expression Trees are never created in the first place (you get a function).
Sorry, I did not follow.
I'm googling around and still kinda not sure.
Select (aka map)
Where (aka filter)
SelectMany (aka Flatmap)
Accumulate (aka fold)
First (with optional predicate function)
Max
Min
GroupBy
Count
And so on. The readme lists them all
The powerful part about LINQ is it builds an AST and there are many different LINQ providers which can translate that AST into some instructions to interact with other systems. The typical one is converting it to SQL commands but you could plug anything into it.
const map = f => xs => ({
[Symbol.iterator]: function * () {
for (const x of xs) {
yield f(x);
}
}
});
xs |> map(x => x * 2);https://medium.com/@aikeru/a-c-linq-to-javascript-translatio...