In trying to stay with the functional style, I am having difficulty understanding when I should prefer:
(-> [1 2 3] reverse last)
over:
(last (reverse [1 2 3]))
When I come across both styles in a project, I find that it breaks my flow since I have to switch between thinking about function compositions and thinking about intermediate value states.
Which should I use at what times?
It has no syntactic meaning. It is just part of the symbol name. In Lisps, the arrow -> (or even just '>') is often used to imply conversion of, or casting of, one type into another.
Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.
I mostly avoid using ->
for one-argument functions; it's a lot more useful when you have multiple-argument functions, because it lets you keep each function next to its "extra args", without the focus object obscuring it.
But also, you don't have to choose one or the other extreme: one of my favorite things about ->
is that it allows you to write the functions in any order at all, and you can use this freedom to write the code in whatever way you think is most readable. For example, perhaps you want to emphasize that you're taking the last of a collection, and in context the fact that it's reversed first is uninteresting:
(last (-> [1 2 3] (reverse)))
Or maybe reversing is important, and last is boring:
(-> (reverse [1 2 3]) (last))
As an aside, note that I wrote (last)
and (reverse)
here rather than just last
and reverse
even though the ->
macro implicitly inserts parentheses for you if you leave them out. This was on purpose: although it's not a terribly popular style, I think it's a good idea to always use parentheses in the forms given to ->
. There are several reasons for this:
(-> (blah) (fn [x] (+ 1 (* x 5))))
expands weirdly, because you are used to thinking that ->
takes a form rather than a function.It looks more regular in a multi-line ->
with some unary functions and other multi-argument functions. For example:
(-> attrs
(update :sessions inc)
frobnicate
save-to-disk
(get :uuid))
Isn't it gross to have those two things in the middle offset, looking different from everything else?
The only argument I have ever seen for omitting the ()
s is that it saves two characters of typing, which is not an argument at all.
My short rule of thumb is "which one is closer to the more declarative english sentence". If you roughly translate them into descriptive sentences, i tend to choose the one that more closely states the thing being produced over the option that describes the process for creating the result. Some people will want to choose the other way. These things are always a matter of taste.
for instance:
(last (reverse [1 2 3]))
is almost "last from reversing 1 2 3"(-> [1 2 3] reverse last)
is sorta like "from 1 2 3, reverse, take last"I find the first more declarative and would choose it in this case, others will choose differently.
Since they are functionally identical, the main thing to consider is which makes the code more readable and maintainable?
This is basically a judgement call.
->
is often useful in situations where:
(f (g x))
style is useful in other circumstances:
->
) or last (with ->>
) argument If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With