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.
Maybe this will look more readable after getting used to it, but I'd much rather be looking at Clojure or Scala for now.
(def articles
[{:title "title1" :author "author1" :tags #{:Java :t2 :t3}}
{:title "title2" :author "author1" :tags #{:Jvxa :t2 :t3}}
{:title "title3" :author "author3" :tags #{:Java :t3}}])
;; find the first article in the collection that has the tag “Java”.
(first (filter #(contains? (:tags %) :Java) articles))
;; ==> {:tags #{:t2 :Java :t3}, :title "title1", :author "author1"}
;;get all the elements that match instead of just the first
(filter #(contains? (:tags %) :Java) articles)
;; ==> ({:tags #{:t2 :Java :t3}, :title "title1", :author "author1"}
{:tags #{:Java :t3}, :title "title3", :author "author3"})
;;group all the articles based on the author.
(group-by :author articles) ;; cheating?
;; ==> {"author1"
[{:tags #{:t2 :Java :t3}, :title "title1", :author "author1"}
{:tags #{:Jvxa :t2 :t3}, :title "title2", :author "author1"}],
"author3"
[{:tags #{:Java :t3}, :title "title3", :author "author3"}]}
;;find all the different tags used in the collections
(apply clojure.set/union (map :tags articles))
;; ==> #{:Jvxa :t2 :Java :t3} (group-by :author articles)
groupBy ((==) `on` author) articles
What's wrong with cheating? :P def topJavaArticle(articles:List[Article]) = articles.find(_.tags.contains("Java"))
def javaArticles(articles:List[Article]) = articles.filter(_.tags.contains("Java"))
def byAuthor(articles:List[Article) = articles.groupBy(_.author)
0: https://news.ycombinator.com/item?id=8874785On first look: "No loops?" "This reduce functions everyhere look ugly."
On second look: "Oh, I can import paralel reduce instead of the single-threaded one?" [1] "Somebody created a library to transparently switch between local reduce and one using hadoop?" [2]
[1] http://clojure.org/reducers [2] https://github.com/aphyr/tesser
I have to read the underscore.js documentation every time I am using it :(.
I think it comes from FP's rooting in mathematics, and math is itself unnecessarily arcane.
But map is confusing: it's also a data structure. "Wait, does map() create a new key-value dictionary?" Filter and collect are okay. Reduce is pretty arcane but tolerable, but the actual operation feels intuitively closer to "categorize" or "group."
My point about math was cultural. Math seems to revel in having its own peculiar and often even domain specific (even within mathematics!) terms and symbols for things.
2. cadr is relatively easy, if you know your assembly (http://en.m.wikipedia.org/wiki/Car_and_cdr#Etymology) :-)
And math has locally defined terms because one cannot give every concept a unique short meaningful memorable name. That is no different in computer science, where 'integer' can include negative numbers or not, may or may not wrap around, can be any number of bits or potentially unlimited, may include minus zero, etc.
If you know IBM 704 assembly...
"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.
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.