public IList<String> getDistinctTags(IEnumerable<Article> articles)
{
return articles.SelectMany(a => a.Tags).Distinct().ToList();
}
The entire LINQ "empire" (.NET 3.5) is built on top of IEnumerable<T> which was around since .NET 2.0. Streams seem to be very artificial; why not rely on Iterable<>?
"Significantly" is a bit dramatic when we are talking about the difference between a single extra method call.
One of the benefits of the Java version is it is easier to understand if you don't have a Java background but do have an FP background. With your C# example you'd need to find the documentation to find out what SelectMany does (which is probably just a helper method that abstracts a map and flatMap call)
The main reason they build these on Stream rather than Iterable is b/c they wanted to include the `parallel()` method, which works via "spliterators" rather than plain old iterators.
In other words, in order to support a gimmick you can actually use in production in a maybe a handful of use cases, they complicated the api for the use cases you hit 99% of the time. Awesome.
The extra verbosity comes from the way java 8 is trying to make functional programming more comfortable while doing as little as possible to the language to do it. When you add all the sugar to make lines more expressive, then you'd hear complaints like the ones Scala gets about having too many ways of doing the same thing.
And yes, that's the entire method definition, signature included. We could have added the return types for documentation if we felt like it.
You might still not like the style, but you can't say it's more verbose than the floor loop.
Backwards compatibility and design philosophy makes sure that Java 8 doesn't go hard enough on the sugar. It's why I am not optimistic of Java's future. There's awesome features out there in newer languages, like proper pattern matching, that Java just won't be able to borrow from functional languages.
This was exactly what I was thinking when I read the code. The goal should not be to remove loops. As a language construct there is nothing wrong with loops itself. The goal should be to make more readable and maintainable code, preferably without increasing verbosity. The functional alternatives posted here are not actually code quality improvements and should not be presented as such.
I guess this is why Microsoft diverged from the "standard" names like 'map', 'collect' and 'filter' in favour of more human-oriented SQL-like LINQ: 'select', 'where', 'toList'.
I have to read the underscore.js documentation every time I am using it :(.
I disagree. Perhaps I'm simply more use to seeing this style, but I feel it's more concise, more maintainable, and more easily built on than the loop-based version. Additionally, I find it to be more semantically clear as to what the expected behavior is, as opposed to reasoning about the loop, especially when filters become complex.
I didn't fully understand this concept until I read your post and found myself disagreeing with you, thinking "what is obstinate talking about? OBVIOUSLY the streams way is way more readable and far less verbose".
The thing is, I can't believe I'm thinking that because I was exactly in your place a few months ago. Since that time, however, I've become very familiar with functional styles and now the Java 8 streams way seems "almost, but not quite right" and the imperative iteration style seems "gratuitously complicated and philosophically wrong... I mean... look at all that special syntax! That mutation! The horror!"
All of this is to say that I'm not quite sure either of us is more correct than the other, but familiarity does seem to cause a profound mental shift.
I think the examples shown here don't really show how it can help- the real win for me is if you already have a function to filter over (like "article.isJava()" or something) it becomes much clearer. It also is eye opening to see or higher order functions like pluck or flatmap that build off these blocks.
Edit- there is a good flatmap example there that I glossed over when first reading. It looks like Java even has serviceable syntax for passing around lambadas like that now too, that is cool.
I don't think the article should have framed it as "let's replace traditional looping constructs," but "let's apply a filter or query to a data set." That's how I've typically seen .NET LINQ written up, and it makes more sense to me.
Java's one of the worst language examples of using FP collections I've seen. Even with hindsight I still find this to be uglier and unnecessarily more verbose than it needs to be.
Can even be shorter without the Optional typing, but it's more readable to be explicit to have them. Dart benefits from having Collection and Stream mixins so you always get a rich API on Dart's collections.
If anyone's interested to comparing FP collections in different languages, I've ported C# 101 LINQ examples in:
- Swift https://github.com/mythz/swift-linq-examples
- Clojure https://github.com/mythz/clojure-linq-examples
- Dart https://github.com/dartist/101LinqSamples
The first example is actually a poor way to do it, IMO. Even if behind the scenes, these are implemented lazily (like C# LINQ), it may not be obvious what's going on to someone else who isn't familiar with the API. I'd opt for the for() loop each time when it's something like halting when you find what you're looking for.
The for loop is actually a poor way to do it, IMO. Even if behind the scenes, these are implemented via conditional gotos (like C), it may not be obvious what
s going on to someone else who isn't familiar with the syntax. I'd opt for the if-goto loop each time when it's something like halting when you find what you're looking for.
To wit: You can expect others to be familiar with basic features of the language and ecosystem, or prepared to learn them.
In the last example's "for" loop, a Set would probably be clearer and more efficient than a List for gathering distinct elements, at least for large data sets. I haven't tried the functional Java yet, but I wonder if using Collectors.toSet() and skipping the distinct() stage would be better?
Any thoughts on performance differences between loops and streams?
My gut says that loops, being a more primitive concept are likely to perform better in most situations. In addition I just find loops easier to reason about, but that is probably purely personal.
I have not looked at the java8 constructs surrounding this.
This is largely implementation specific. For instance, the .net LINQ to object implementations are largely syntactic sugar around loops (that is they compile to the same thing). Similarly, for loops are frequently just syntactic sugar around while loops.
It should be noted that one must assume a performance penalty when calling stream() on a Collection, as doing so creates a new Stream object. Within inner loops and for small Collections I recommend avoiding stream() altogether if performance is a concern. Furthermore, while tempting, Stream.parallel() should only be called when it is certain that the additional cost of a ForkJoinPool instance creation can be amortized over the duration of the lambda's runtime. With that said, I welcome Streams and other FP concepts to Java.
The examples feel very much like Ruby to me. In a good way. This sort of chaining of operations also feels very natural for someone thinking in terms of a chain of Unix commands piped together.
However, having that explicit "stream()" signifier is a very Java-y thing to do and appears to ask the programmer to decide how best to compile the given line. I would expect the compiler should be doing that work for us.
This demonstrates a major problem with development of Java since the Collections work (which was fantastic): the libraries suffer from over-engineering and surface far too much implementation flavor in the API.
Who cares about streams? Who cares about Optional? We just want to filter a list in a clear, terse manner. (Some people do care about streams and Optional, and I wish them well, but that's orthogonal to the question at hand.)
Consider the examples given. Here they are implemented in Gosu:
(I cheated a bit on the last one by just using a Set, but that's more appropriate and communicates the uniqueness of the elements in the collection to the API consumer.)
Beyond the dot-star flatmap operator, there isn't anything very fancy going on: just closures being passed to methods, returning familiar classes that don't require additional transformation to pass on to the rest of the world.
It's too bad, because this is certainly good enough. As Jack Nicholson said: What if this... is as good as it gets?
public final class Article{
public final String title;
public final String author;
public final List<String> tags;
public Article(String title, String author, List<String> tags) {
this.title = title;
this.author = author;
this.tags = tags;
}
}
Getters don't seem very useful on an immutable object.
If you ever want to change the implementation of Article, you'd break anyone that was using that part of your API. If you use getters, you can change your implementation without breaking the consumers of your API.
For instance, let's say that you don't want to store the author's name as a string anymore, and want to store a reference to an Author object. If you have a getAuthor() method, you can change it from a simple getter to instead call author.getName(), preserving your public-facing API.
Constructing a collection of things is just one case where a for loop is nice. Would be nice to see some more interesting examples of how we can use streams.
No its the price you pay, when you improperly implement generics and do not have polymorphic functions. In other words its the price you pay, if you ignore the developments in computer science in the last 10 years and invent a crippled terrible language in 1995 (roughly the same time OCaml came out) and push it onto the world with success, because you are a big corporation. At the point they introduced generics, there really was no way you should have gotten it wrong, but they did... Incidentally I think it's a failure of the Free Software movement, that they did not do much to actually move towards a modern statically typed language (or family of languages) that was not burdened by corporate policy.
In Haskell the type of map is
map :: Functor f => f a -> (a -> b) -> f b
(actually for historical reasons its called fmap, but nevermind). Here f a in Java could be something like Stream<a>, that is something that contains things of type a.
It's the price to pay for implementing generics via type erasure, not for getting functional features in a statically typed language. http://stackoverflow.com/a/24421331
"Static typing" is not quite where those come from – mapTo{Int,Long,Double} exist because of Java's primitive/object dichotomy. You can just write map and do the same calculation, but then your lambda will have type ? -> Long (vs. the http://docs.oracle.com/javase/8/docs/api/java/util/function/... , which provides ? -> long).
It's mostly the same semantics, but it costs an extra object for each element in your list. If the only thing you're going to do is sum those longs or serialize them over the wire or something, the extra 6 characters have a significant impact on run-time. There's lots of solutions in this space (e.g. Rust is a static language that uses a lot of "zero-cost" abstractions like tagged pointers that it can prove are safe specifically because of the static types), but Java's "make the programmer do it explicitly" isn't so bad for 1995.
Too much architecture for what? For bankings? For medical devices? For the safety-critical avionics software? For high-frequency trading? For manufacturing control? For weapon-systems? For power-plant command and control? For government ERP running on mainframes? Because Java is used for all of these things.
Yes, some bits might be too architected for your MVP web app that you're going to re-write in a year (and even that depends mostly on the libraries you're using; there are plenty of lean libraries for the web-startup crowd).
[+] [-] anton_gogolev|11 years ago|reply
Oh, and no "yield" in Java.
[+] [-] djb_hackernews|11 years ago|reply
One of the benefits of the Java version is it is easier to understand if you don't have a Java background but do have an FP background. With your C# example you'd need to find the documentation to find out what SelectMany does (which is probably just a helper method that abstracts a map and flatMap call)
[+] [-] jfager|11 years ago|reply
In other words, in order to support a gimmick you can actually use in production in a maybe a handful of use cases, they complicated the api for the use cases you hit 99% of the time. Awesome.
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] azurelogic|11 years ago|reply
[+] [-] peterashford|11 years ago|reply
[+] [-] olavgg|11 years ago|reply
articles.findAll{ it.tags.contains("Java") }
All I've done is adding groovy.jar
[+] [-] obstinate|11 years ago|reply
[+] [-] hibikir|11 years ago|reply
in scala, finding the first article looks like:
def topJavaArticle(articles:List[Article]) = articles.find(_.tags.contains("Java"))
which returns an Option[Article], so it handles the null check.
Since List in scala already handles functional constructs directly, then the filter example is just as easy:
def javaArticles(articles:List[Article]) = articles.filter(_.tags.contains("Java"))
group by author?
def byAuthor(articles:List[Article) = articles.groupBy(_.author)
And yes, that's the entire method definition, signature included. We could have added the return types for documentation if we felt like it.
You might still not like the style, but you can't say it's more verbose than the floor loop.
Backwards compatibility and design philosophy makes sure that Java 8 doesn't go hard enough on the sugar. It's why I am not optimistic of Java's future. There's awesome features out there in newer languages, like proper pattern matching, that Java just won't be able to borrow from functional languages.
[+] [-] remon|11 years ago|reply
[+] [-] CmonDev|11 years ago|reply
I have to read the underscore.js documentation every time I am using it :(.
[+] [-] jimktrains2|11 years ago|reply
[+] [-] breckinloggins|11 years ago|reply
"Elegance and familiarity are orthogonal."
I didn't fully understand this concept until I read your post and found myself disagreeing with you, thinking "what is obstinate talking about? OBVIOUSLY the streams way is way more readable and far less verbose".
The thing is, I can't believe I'm thinking that because I was exactly in your place a few months ago. Since that time, however, I've become very familiar with functional styles and now the Java 8 streams way seems "almost, but not quite right" and the imperative iteration style seems "gratuitously complicated and philosophically wrong... I mean... look at all that special syntax! That mutation! The horror!"
All of this is to say that I'm not quite sure either of us is more correct than the other, but familiarity does seem to cause a profound mental shift.
[+] [-] mattnewton|11 years ago|reply
Edit- there is a good flatmap example there that I glossed over when first reading. It looks like Java even has serviceable syntax for passing around lambadas like that now too, that is cool.
[+] [-] TYPE_FASTER|11 years ago|reply
[+] [-] thescrewdriver|11 years ago|reply
[+] [-] codygman|11 years ago|reply
https://news.ycombinator.com/item?id=8878421
[+] [-] mythz|11 years ago|reply
E.g. same Example in Dart:
Can even be shorter without the Optional typing, but it's more readable to be explicit to have them. Dart benefits from having Collection and Stream mixins so you always get a rich API on Dart's collections.If anyone's interested to comparing FP collections in different languages, I've ported C# 101 LINQ examples in:
[+] [-] markc|11 years ago|reply
Here's the Clojure version:
[+] [-] kuschku|11 years ago|reply
I’d have to look up what .expand, .where etc means, while Java just uses the standard FP names that every CompSci student knows.
[+] [-] gavinpc|11 years ago|reply
Nice autologism.
[+] [-] edgyswingset|11 years ago|reply
[+] [-] anon4|11 years ago|reply
To wit: You can expect others to be familiar with basic features of the language and ecosystem, or prepared to learn them.
[+] [-] aaronetz|11 years ago|reply
[+] [-] SeanDav|11 years ago|reply
My gut says that loops, being a more primitive concept are likely to perform better in most situations. In addition I just find loops easier to reason about, but that is probably purely personal.
[+] [-] kasey_junk|11 years ago|reply
This is largely implementation specific. For instance, the .net LINQ to object implementations are largely syntactic sugar around loops (that is they compile to the same thing). Similarly, for loops are frequently just syntactic sugar around while loops.
[+] [-] acaloiar|11 years ago|reply
[+] [-] skywhopper|11 years ago|reply
However, having that explicit "stream()" signifier is a very Java-y thing to do and appears to ask the programmer to decide how best to compile the given line. I would expect the compiler should be doing that work for us.
[+] [-] carsongross|11 years ago|reply
Who cares about streams? Who cares about Optional? We just want to filter a list in a clear, terse manner. (Some people do care about streams and Optional, and I wish them well, but that's orthogonal to the question at hand.)
Consider the examples given. Here they are implemented in Gosu:
(I cheated a bit on the last one by just using a Set, but that's more appropriate and communicates the uniqueness of the elements in the collection to the API consumer.)Beyond the dot-star flatmap operator, there isn't anything very fancy going on: just closures being passed to methods, returning familiar classes that don't require additional transformation to pass on to the rest of the world.
It's too bad, because this is certainly good enough. As Jack Nicholson said: What if this... is as good as it gets?
[+] [-] Proleps|11 years ago|reply
[+] [-] organsnyder|11 years ago|reply
For instance, let's say that you don't want to store the author's name as a string anymore, and want to store a reference to an Author object. If you have a getAuthor() method, you can change it from a simple getter to instead call author.getName(), preserving your public-facing API.
[+] [-] thescrewdriver|11 years ago|reply
Scala version of your code:
[+] [-] V-2|11 years ago|reply
Properly implemented getTags should create a copy of tags so that fiddling with the returned value doesn't affect the object.
[+] [-] spullara|11 years ago|reply
https://github.com/jmoy/norvig-spell/blob/master/java/src/ma...
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] glifchits|11 years ago|reply
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] orbifold|11 years ago|reply
In Haskell the type of map is
map :: Functor f => f a -> (a -> b) -> f b
(actually for historical reasons its called fmap, but nevermind). Here f a in Java could be something like Stream<a>, that is something that contains things of type a.
[+] [-] philippelh|11 years ago|reply
[+] [-] ac_386|11 years ago|reply
It's mostly the same semantics, but it costs an extra object for each element in your list. If the only thing you're going to do is sum those longs or serialize them over the wire or something, the extra 6 characters have a significant impact on run-time. There's lots of solutions in this space (e.g. Rust is a static language that uses a lot of "zero-cost" abstractions like tagged pointers that it can prove are safe specifically because of the static types), but Java's "make the programmer do it explicitly" isn't so bad for 1995.
[+] [-] wodude|11 years ago|reply
tagList = articles.tags.@flatten.@unique.@sort
[+] [-] nsxwolf|11 years ago|reply
[+] [-] Beltiras|11 years ago|reply
[+] [-] pron|11 years ago|reply
Yes, some bits might be too architected for your MVP web app that you're going to re-write in a year (and even that depends mostly on the libraries you're using; there are plenty of lean libraries for the web-startup crowd).
[+] [-] hristov|11 years ago|reply
[+] [-] kasey_junk|11 years ago|reply
[+] [-] bitL|11 years ago|reply
[+] [-] codygman|11 years ago|reply
http://tech.pro/tutorial/2011/functional-javascript-part-4-f....
https://www.haskell.org/haskellwiki/Currying