
Learning Clojure: comparing with Java streams - nfrankel
https://blog.frankel.ch/learning-clojure/5/
======
vlaaad
Also, this bit:

    
    
        (->> justice-league
          (map #(:vehicles %))
          (filter #(not (nil? %)))
          (flatten))
    

can be simplified to just `(mapcat :vehicles justice-league)`

~~~
adambard
A few notes on why this is so:

\- Keywords (i.e. `:vehicles`) can be used as getter functions, as covered in
the OP.

\- mapcat (like most collection functions in clojure) treats `nil` as an empty
sequence

------
Sharlin
The equivalent of `flatMap` is not `flatten`. Semantically `flatMap` is
equivalent to `(comp flatten map)` – hence the name! – but Clojure does offer
it as a single function named `mapcat` (”map and catenate”).

~~~
jetrink
> Semantically `flatMap` is equivalent to `(comp flatten map)`

With the caveat that `flatten` is recursive and can handle a mix of
collections and non-collections, while `mapcat` is non-recursive and every
value returned by the mapcatted function must be a collection or nil.

(Identity returns its argument.)

    
    
        (flatten [1 [2 3] [[4 5 6]]])
        > [1 2 3 4 5 6]
    
        (mapcat identity [1 [2 3] [[4 5 6]]])
        > IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
    
        (mapcat identity [[1] [2 3] [[4 5 6]]])
        > (1 2 3 [4 5 6])
    
        (flatten [nil [1 2 3]])
        > (nil 1 2 3)
    
        (mapcat identity [nil [1 2 3]])
        > (1 2 3)

~~~
Sharlin
Thanks, I wasn’t aware that flatten is recursive.

~~~
TeMPOraL
That's sort of implied by the name. A non-recursive flatten wouldn't really
flatten much, and would be called join, or concatenate.

~~~
Sharlin
The flatten functions I know from statically typed languages (eg. Rust, Scala)
only flatten one level. So flatMap is actually more or less equivalent to
(flatten . map).

~~~
TeMPOraL
Interesting. My experience is the opposite - in dynamically typed languages
like Erlang, Clojure or Common Lisp, flatten is understood to _flatten_ lists,
and flatmap is something else entirely - from what I gather, it's Scala that
made the weird choice of naming a concatenation function `flatten`, thus
creating `flatMap`[0], and then people started borrowing the name.

From this I provisionally conclude that "flatten" being non-recursive is an
artifact of Scala confusing things; not an unusual thing in my experience with
this language, but this time it seems to have gotten out of hand, instead of
being contained within Scala ecosystem.

\--

[0] - Even Haskell calls this `concatMap`, see [1].

[1] - [https://stackoverflow.com/questions/49843262/where-does-
the-...](https://stackoverflow.com/questions/49843262/where-does-the-word-
flatmap-originate-from)

------
roenxi
Not a lot of comments here for the number of upvotes, but the trend of the
comments strikes me as lopsidedly negative and I'd like to throw in something
positive to the mix - Clojure needs more people publishing beginner friendly
code even if it isn't using some specific function that does the same thing
with less typing. So, y'know, I'm really glad we've got people out there who
are documenting how they manage to get things to work under the new and
generally unfamiliar paradigm of "practically functional". Especially since
Clojure's Achilles heel is the combined double-whammy of its incoherent error
messages and the learning curve for a beginner trying to pick up how to do
common tasks in a world with 'no variables'.

We have a programmer with >15 years experience, who is learning a new language
and in the process producing code that is neat, functional (in the sense of no
bugs) and not unreasonably verbose. Clojure is a language that is dense with
clever ideas, it takes a lot longer than a few months to get good at using
them if they are new. The terse comments throwing out improvements or making
corrections seem a little harsh, I'd hoped to see people talking more about
concepts than naming functions.

~~~
nfrankel
Author here. Many thanks for your encouragements!

Actually, I didn't feel any comments were particularly harsh. I'm learning
from most of them. This is exactly the feedback I'm searching for: idiomatic
Clojure.

------
outworlder
> and Clojure’s syntax is pretty limited

Erm, what? I am confused by the use of the word 'limited' in this context.

~~~
nfrankel
I confirm: the syntax is limited. There are only a few keywords and langage
constructs. The power is in the number of functions made available, or the
ones you create for yourself.

~~~
outworlder
Doesn't Clojure have reader macros? The whole point of lisps is that you can
extend them.

~~~
didibus
Actually, Clojure does not have reader macros, because it was thought to be
the biggest cause of the lisp curse.

~~~
grzm
Nit: Clojure does not have _user-defined_ reader macros. There are quite a few
built-in reader macros, including #? added for reader conditionals.

~~~
didibus
Fair enough, though definitly a nitpick :p

------
madmax96
It seems strange to me that `(map extract-name justice-league)` isn't listed
under "idiomatic improvements." Supposing you already had `extract-name`
defined for some reason, it's cleaner and simpler to just map that unary
function than wrap that function in an anonymous function - whether that
wrapping is done with the `#(...)` reader syntax or explicitly constructed
with `fn`.

~~~
Sharlin
Or indeed `(map :name foo)` instead of the suggested (and redundant,
linenoisy) `(map #(:name %) foo)`.

------
zmmmmm
Out of curiosity, can anybody comment whether the Clojure versions of these
are "lazy" in the sense that if you only take the first element from the
result, does the the stream process all the results or just the first? eg:

    
    
        (first (map #(extract-name %) justice-league))
    

Does it map all the elements or only the first? This is actually the key
aspect of streams, and more important in many ways than whatever syntax sugar
is available to invoke the operations.

~~~
jimbokun
In Clojure, all of these are lazy by default.

------
didibus
Did I miss the Java counterpart of the examples?

