Most Developers don't care if an academic turing-complete brainfuck can be made to run on Android, they care if they're able to be more productive and create more readable and maintainable code.
Running on Android was a constraint for this library, the natural approach to functional programming in Java 8 would've been to use Java 8's new stream API, but as that's not available on Android we fallback to using what currently does work on Android - which is the point of this library.
Just because they can translate doesn't mean it's good. If you check out this example: https://github.com/mythz/java-linq-examples/blob/master/READ... it converts 6 lines of readable C# to 20 lines of bad formed Java. It would be a lot nicer to see a Java 8 translation - a lot more comparable.
It's better to write code that is amenable to being understood when read. These Java forms accomplish things 'the LINQ way' but at the expense of completely obscuring the intent of the code. LINQ makes sense in C# because the syntax supports a mapping from a declarative query to stream-oriented operations. Without the syntactic support, it becomes unreadable.
I usually prefer the chaining method extensions for collections myself... Even then it's far easier to follow than the Java examples, or even the Java8 comments... but that's mostly personal preference. I'm also not sure how/if this has an IQueryable interface equivalent, which is pretty cool as well.
You can't use Java 8 on Android but you can get a lot of its features. RetroLambda library will get you lambdas and streamsupport provides backported Streams API.
Makes you wonder why that Java version (the android version) and not Java 8 is used in the comparison. The only conclusion I make from the examples is that I never want to do Android+Java development until Java 8 at least...
> Humans benefit from the redundancy of the type declaration in two ways. First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns. Second, the redundancy allows the programmer to declare the intended type, and thereby benefit from a cross check performed by the compiler.
With "Humans Benefit" cited as the rationale, which seems to contradict the behavior in IntelliJ IDE's which automatically collapses boiler-plate code into a "typeless lambda format" of what the source code would've looked like if Java had lambdas.
To be fair a lot more type inference is done in Java 8 (it's not just lambdas), it's just not available for Android.
And Java tends to do type inference on the right hand side instead of the left hand side, so things look a lot cleaner if you chain them instead of assigning them to intermediate variables.
> And Java tends to do type inference on the right hand side instead of the left hand side, so things look a lot cleaner if you chain them instead of assigning them to intermediate variables.
This example is chained, i.e. there are no intermediate variables.
> The customers and customerOrderGroups are intermediate.
The `customers` and `customerOrderGroups` are variables in each of the examples. Which is the point of a close 1:1 translation between different languages. linq43 groups Customer Orders in a single expression by Year/Month into an anonymous structure. This just doesn't translate very well to Java 7 which lacks many of the language features to make it more readable.
Of course it can be rewritten using Java 8 streams which is much better suited for functional programming courtesy of lambda's (which is the primary cause for much of the boiler-plate). But until it's supported in Android we have to make do with what we have.
Which looks like it's a post-build Maven and Gradle plugin to back port Java 8 features to be able to run on Java 7 Android and JVM runtime. Not sure if there are any disadvantages with this approach (debugging?) but it's an interesting alternative.
The fact that Java's the only language that infers type on the right hand side of an equals sign should tell you something. Every other language thinks that's the wrong side.
You know, I am pretty impressed I got down-voted. Every time I see someone mock Java or PHP around here, people are quick to point out no language is perfect and personal biases are not great for reasoned discussioned. I thought there was no perfect language?
C is also boring, but I assumed mocking it here would be asking for free kicks to the face. Apparently, I was wrong.
Ask any developer and they will have their favorite language.
Java isn't bad (not my favorite). I like it better than C#, but then I hate Windows, so of course I would say that.
(Edit: Yes, I know C# is on more platforms than just Windows. If we're getting completely technical.)
Not a great fan of LINQ if I'm honest. Sure it's terse and powerful but with it comes great responsibility and a relatively large pile of tasty landmines. Three regular problems I see:
FirstOrDefault being passed 2 rows = non-determinism. This one is always fun when your SQL server doesn't guarantee the order of rows returned so every time you hit .FirstOrDefault you get a different item. Never happens in dev due to the tiny datasets fitting in a single page.
There's also Single being passed 2 rows = crash. SingleOrDefault being passed >1 rows = crash.
And when it does crash it's entirely not helpful because the lambda block reports no state information in the stack. Inevitably this leads to adding precondition checks to avoid "Hey everyone, one of the 20 LINQ expressions in this method blew with more than one element in the sequence".
Then there's the fact that you don't know the size of the set of data nor really think about it. So if someone passes in a collection of 10 rows in dev and it works out that it's O(N^2) and has a random .ToList() in it then you're up shit creek in the memory and time departments when that 10000 items collection appears in production.
None of this unique to LINQ but it hides a lot of problems behind a wall of pain.
> FirstOrDefault being passed 2 rows = non-determinism... There's also Single being passed 2 rows = crash. SingleOrDefault being passed >1 rows = crash.
Which is the point of the different API's, you get to pick the behavior you want. Don't want it to crash? pick `FirstOrDefault`. A formal API is better than manually codifying the elected behavior each time, making it tedious to infer the intent each time whilst being susceptible to human errors manually copy+pasting imperative code.
The problem is that it's a complex trade off whichever way you turn. Knowing which trade off to make is difficult for many people as it requires knowing all of the assumptions and conditions that are before and after the call as well as how the expression will react under all conditions. Add complex predicates to that,which the language encourages and it's pain.
Except your example isn't a complex trade off -- it's just how it works. FirstOrDefault() returns the first or a default. It's right in the name? Which is first? Well you better provide an order if you care. If you don't care, don't provide an order. There is nothing complex about that at all.
It almost seems like your looking to blame Linq for what is clearly poor database and project design. If you have poor design you're going to have problems no matter what.
Objects.Where(el => el.Id == myRequestedId).FirstOrDefault().Name; //Crashes when there is no result, but this has been handled by the null operator in c# 5.0 => Objects.Where(el => el.Id == myRequestedId).FirstOrDefault()?.Name; The issues was not Linq, it is/was c#
Single() is when there is absolute only 1 result. > 1 result is not expected ( i'll admit that i never use Single or SingleOrDefault() )
And using paging is the same thing in linq as it is in SQL. If you don't know how Linq works, you should learn it. What you should know is that .ToList() hits the database, so when you query all the objects, you get all the objects. Limit your query before the database query :)
Here are the results of page 4 with 10 items : Objects.Skip(10 x 3).Take(10).ToList(); //fetches 10 items = paging in the query
Here is how someone could write it without knowing Linq: Objects.ToList().Skip(10 x 3).Take(10);//fetches all the items and does the paging on the list.
As long as your DataType is an IQueryable<T>, it doesn't hits the database. When it's a List<T> (eg. when calling .ToList()) it's too late
But both you and I know that is bad design. The operator is a shortcut for simple scenarios, but in this case if a decision has to be made then you should be handling the null checks yourself and making those decisions explicitly.
A line like that is just lazy. I'm more keen to blame the developer and not the language. Striking nails with your fists as they say.
With all respect, it doesn't sound like you've taken the time to understand LINQ:
- Single and SingleOrDefault crashing with >1 row is a feature, not a bug. It means that your expectation that your filter is unique was incorrect. In that scenario, a crash is what you want. It's like when we use asserts. Similarly ..
- First and FirstOrDefault are only really appropriate after an OrderBy, i.e. to get the top item of a particular ranking. Using them anywhere else is almost always incorrect, and in the very few cases where it isn't, needs a comment to explain why, or I'll bounce your code back to you in review :).
LINQ is very expressive (you can do a lot with a little bit of code), especially with the embedded (non-fluent) syntax. That means that, yes, you can write some non-optimal code without realizing it, because it's only a few lines. On the flip side, you can also refactor that code quickly into more optimal patterns. By comparison, old-school iterating through collections is often much more difficult to refactor.
I understand LINQ very well (right down to code generation and expression trees) but unfortunately I work with a lot of other people's code.
Perhaps that is the problem? :-)
More seriously, the thing has really leaky abstraction boundaries which is a main pain point. I have to write and manage a lot of boring enterprise code and creativity and expressiveness go a long way to introducing a lot of additional work.
LINQ is not where the leaky abstraction is, IEnumerable is. IEnumerable makes virtually no guarantees about anything, including order, finiteness, side effects and speed/efficiency.
This is actually one of the main reasons that the BCL team introduced the IReadOnlyXXX<> interfaces - over the years, many .NET developers trying to do the right thing and express intent in their code had taken to using IEnumerable<> in method signatures to signify that the method would not modify the list/collection... but an IEnumerable doesn't have to be a list/collection, it can be infinite! In trying to do the right thing and be expressive with intent, lots and lots of people were unwittingly losing the expression of another (very important) assumption.
Any time you are wrangling any kind of enumerable in code, you have to think about its guarantees and assumptions. LINQ's expressiveness might make it easier to forget this but it's still the rule - if I hand you a "malicious IEnumerable", it doesn't matter if you use LINQ or a couple of foreach loops, it's going to launch the missiles on every call to MoveNext() regardless.
If you are trying to express intent in your code, IEnumerable is almost never what you want to use.
> None of this unique to LINQ but it hides a lot of problems behind a wall of pain.
There is your problem and it isn't LINQ. You'd have the same problem with C# without it. LINQ is a good declarative tool when declarative programming makes sense : data pipelines. And LINQ is 'lazy'...
You should be glad that an imperative language succeeded in getting declarative features. And that makes C# better than the competition in the same space, even though I never use MS techs.
How should First work? How is this different than manually doing "SELECT TOP 1" or using LIMIT, but not specifying an ORDER BY? What a strange complaint.
And Single is supposed to crash if there's more than one row. Again, it's directly like selecting "TOP 2" then doing a check to make sure there isn't a second row. Your complaint seems like complaining that using a "while" conditional instead of an "if" can lead to trouble.
Seriously I'd love to know what code you are writing where you weren't sure if you wanted First or Single, decided Single (thus enforcing that data integrity rule), then decided it's the library's fault for doing exactly what you asked.
Man I wish Android had LINQ, and the Java language support it relies on in C#. This is at or very near the top of the list of Things That Would Have Made Android Better especially since non-game Android coding is so database-centric.
True, but it means taking the rest of Xamarin, too. While Xamarin is brilliant, and by far the best cross-platform SDK I've seen for any set of targets, it's still riskier than native platform development for most developers. I'd use it for a client who has a C#-oriented in-house team and the ability and resources to commit to overcoming the vicissitudes of cross-platform development, but I wouldn't just to get LINQ.
>There's also Single being passed 2 rows = crash. SingleOrDefault being passed >1 rows = crash.
It's not a crash, it's an exception. You can catch an exception. But, as others have said, you could instead call the function that doesn't throw an exception if having more than one result isn't an error.
Wow, that just looks hideous... at first I was hoping this was some kind of Java preprocessor for linq constructs... but it's just plain ugly.. it's even worse than the method chaining you can do in C# with lambdas, which I prefer over linq syntax...
var result = someEnumerable.Select(x => x.Bar < 5).ToList()
Still a lot cleaner than the type specific mess in the examples... I think sticking to for-loops is probably cleaner than that mess.
I don't mean to be critical here, it's just I don't think it's really any better than without the "LINQ" for Java in this case.
41 comments
[ 2.5 ms ] story [ 91.9 ms ] threadThat is also totally not the point.
Running on Android was a constraint for this library, the natural approach to functional programming in Java 8 would've been to use Java 8's new stream API, but as that's not available on Android we fallback to using what currently does work on Android - which is the point of this library.
I think it's an inner join, not a cross join. SelectMany() produces a cross join.
If that's "bad-formed Java" what's a better functional form? Or do you mean for Java it's better to use an imperative-style with mutating collections?
https://gist.github.com/mythz/7e263820332a0fcea766
What's interesting was that Type Inference was actually considered and rejected because it was considered "un-Java Like" http://programmers.stackexchange.com/a/184183/14025:
> Humans benefit from the redundancy of the type declaration in two ways. First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns. Second, the redundancy allows the programmer to declare the intended type, and thereby benefit from a cross check performed by the compiler.
With "Humans Benefit" cited as the rationale, which seems to contradict the behavior in IntelliJ IDE's which automatically collapses boiler-plate code into a "typeless lambda format" of what the source code would've looked like if Java had lambdas.
And Java tends to do type inference on the right hand side instead of the left hand side, so things look a lot cleaner if you chain them instead of assigning them to intermediate variables.
This example is chained, i.e. there are no intermediate variables.
I would expect in reality to do something like this in Java 8 (Disclaimer probably not exactly correct syntax, not near a Java compiler)
Further it looks like we should probably have domain types for MonthGroup/OrderGroup etc instead of tuples, which would make it still cleanerThe `customers` and `customerOrderGroups` are variables in each of the examples. Which is the point of a close 1:1 translation between different languages. linq43 groups Customer Orders in a single expression by Year/Month into an anonymous structure. This just doesn't translate very well to Java 7 which lacks many of the language features to make it more readable.
Of course it can be rewritten using Java 8 streams which is much better suited for functional programming courtesy of lambda's (which is the primary cause for much of the boiler-plate). But until it's supported in Android we have to make do with what we have.
----
Should also give a shout-out to RetroLambda which I just discovered in the comments: https://github.com/orfjackal/retrolambda
Which looks like it's a post-build Maven and Gradle plugin to back port Java 8 features to be able to run on Java 7 Android and JVM runtime. Not sure if there are any disadvantages with this approach (debugging?) but it's an interesting alternative.
Another option would be to just use a different JVM language, Kotlin looks the most appealing to me which supports Android and has good Java interoperability: http://kotlinlang.org/docs/tutorials/kotlin-android.html
One could also go for implementing a more Linq-esq style in Java. Something like http://benjiweber.co.uk/blog/2013/12/28/typesafe-database-in... could be implemented on top of collections as well as databases.
I think.
And I know, I know, bash is even less elegant and more boring. Haha.
C is also boring, but I assumed mocking it here would be asking for free kicks to the face. Apparently, I was wrong.
Ask any developer and they will have their favorite language. Java isn't bad (not my favorite). I like it better than C#, but then I hate Windows, so of course I would say that.
(Edit: Yes, I know C# is on more platforms than just Windows. If we're getting completely technical.)
FirstOrDefault being passed 2 rows = non-determinism. This one is always fun when your SQL server doesn't guarantee the order of rows returned so every time you hit .FirstOrDefault you get a different item. Never happens in dev due to the tiny datasets fitting in a single page.
There's also Single being passed 2 rows = crash. SingleOrDefault being passed >1 rows = crash.
And when it does crash it's entirely not helpful because the lambda block reports no state information in the stack. Inevitably this leads to adding precondition checks to avoid "Hey everyone, one of the 20 LINQ expressions in this method blew with more than one element in the sequence".
Then there's the fact that you don't know the size of the set of data nor really think about it. So if someone passes in a collection of 10 rows in dev and it works out that it's O(N^2) and has a random .ToList() in it then you're up shit creek in the memory and time departments when that 10000 items collection appears in production.
None of this unique to LINQ but it hides a lot of problems behind a wall of pain.
From extensive experience; there be dragons.
Which is the point of the different API's, you get to pick the behavior you want. Don't want it to crash? pick `FirstOrDefault`. A formal API is better than manually codifying the elected behavior each time, making it tedious to infer the intent each time whilst being susceptible to human errors manually copy+pasting imperative code.
It almost seems like your looking to blame Linq for what is clearly poor database and project design. If you have poor design you're going to have problems no matter what.
Single() is when there is absolute only 1 result. > 1 result is not expected ( i'll admit that i never use Single or SingleOrDefault() )
And using paging is the same thing in linq as it is in SQL. If you don't know how Linq works, you should learn it. What you should know is that .ToList() hits the database, so when you query all the objects, you get all the objects. Limit your query before the database query :)
Here are the results of page 4 with 10 items : Objects.Skip(10 x 3).Take(10).ToList(); //fetches 10 items = paging in the query
Here is how someone could write it without knowing Linq: Objects.ToList().Skip(10 x 3).Take(10);//fetches all the items and does the paging on the list.
As long as your DataType is an IQueryable<T>, it doesn't hits the database. When it's a List<T> (eg. when calling .ToList()) it's too late
The coalesce operator in C# 5 is welcome but then introduces another problem:
If x is null, why and where?This is all in-memory manipulation of data. Mainly rules engine stuff for us.
A line like that is just lazy. I'm more keen to blame the developer and not the language. Striking nails with your fists as they say.
- Single and SingleOrDefault crashing with >1 row is a feature, not a bug. It means that your expectation that your filter is unique was incorrect. In that scenario, a crash is what you want. It's like when we use asserts. Similarly ..
- First and FirstOrDefault are only really appropriate after an OrderBy, i.e. to get the top item of a particular ranking. Using them anywhere else is almost always incorrect, and in the very few cases where it isn't, needs a comment to explain why, or I'll bounce your code back to you in review :).
LINQ is very expressive (you can do a lot with a little bit of code), especially with the embedded (non-fluent) syntax. That means that, yes, you can write some non-optimal code without realizing it, because it's only a few lines. On the flip side, you can also refactor that code quickly into more optimal patterns. By comparison, old-school iterating through collections is often much more difficult to refactor.
Perhaps that is the problem? :-)
More seriously, the thing has really leaky abstraction boundaries which is a main pain point. I have to write and manage a lot of boring enterprise code and creativity and expressiveness go a long way to introducing a lot of additional work.
This is actually one of the main reasons that the BCL team introduced the IReadOnlyXXX<> interfaces - over the years, many .NET developers trying to do the right thing and express intent in their code had taken to using IEnumerable<> in method signatures to signify that the method would not modify the list/collection... but an IEnumerable doesn't have to be a list/collection, it can be infinite! In trying to do the right thing and be expressive with intent, lots and lots of people were unwittingly losing the expression of another (very important) assumption.
Any time you are wrangling any kind of enumerable in code, you have to think about its guarantees and assumptions. LINQ's expressiveness might make it easier to forget this but it's still the rule - if I hand you a "malicious IEnumerable", it doesn't matter if you use LINQ or a couple of foreach loops, it's going to launch the missiles on every call to MoveNext() regardless.
If you are trying to express intent in your code, IEnumerable is almost never what you want to use.
There is your problem and it isn't LINQ. You'd have the same problem with C# without it. LINQ is a good declarative tool when declarative programming makes sense : data pipelines. And LINQ is 'lazy'...
You should be glad that an imperative language succeeded in getting declarative features. And that makes C# better than the competition in the same space, even though I never use MS techs.
And Single is supposed to crash if there's more than one row. Again, it's directly like selecting "TOP 2" then doing a check to make sure there isn't a second row. Your complaint seems like complaining that using a "while" conditional instead of an "if" can lead to trouble.
Seriously I'd love to know what code you are writing where you weren't sure if you wanted First or Single, decided Single (thus enforcing that data integrity rule), then decided it's the library's fault for doing exactly what you asked.
It's not a crash, it's an exception. You can catch an exception. But, as others have said, you could instead call the function that doesn't throw an exception if having more than one result isn't an error.
I don't mean to be critical here, it's just I don't think it's really any better than without the "LINQ" for Java in this case.