Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

-> operator in Clojure

Is the -> operator in Clojure (and what is this operator called in Clojure-speak?) equivalent to the pipeline operator |> in F#? If so, why does it need such a complex macro definition, when (|>) is just defined as

let inline (|>) x f = f x 

Or if not, does F#'s pipeline operator exist in Clojure, or how would you define such an operator in Clojure?

like image 699
Dax Fohl Avatar asked May 26 '11 21:05

Dax Fohl


2 Answers

No, they are not the same. Clojure doesn't really have a need for |> because all function calls are enclosed in lists, like (+ 1 2): there's no magic you could do to make 1 + 2 work in isolation.1

-> is for reducing nesting and simplifying common patterns. For example:

(-> x (assoc :name "ted") (dissoc :size) (keys)) 

Expands to

(keys (dissoc (assoc x :name "ted") :size)) 

The former is often easier to read, because conceptually you're performing a series of operations on x; the former code is "shaped" that way, while the latter needs some mental unraveling to work out.

1 You can write a macro that sorta makes this work. The idea is to wrap your macro around the entire source tree that you want to transform, and let it look for |> symbols; it can then transform the source into the shape you want. Hiredman has made it possible to write code in a very Haskell-looking way, with his functional package.

like image 90
amalloy Avatar answered Sep 30 '22 12:09

amalloy


It's called the "thread" operator. It's written as a macro as opposed to a normal function for performance reasons and so that it can provide a nice syntax - i.e. it applies the transformation at compile time.

It's somewhat more powerful than the |> operator you describe, as it's intended to pass a value through several functions, where each successive value is "inserted" as the first parameter of the following function calls. Here's a somewhat contrived example:

(-> [1]      (concat [2 3 4])      (sum)      ((fn [x] (+ x 100.0)))) => 110.0 

If you want to define a function exactly like the F# operator you have described, you can do:

(defn |> [x f] (f x))  (|> 3 inc) => 4 

Not sure how useful that really is, but there you are anyway :-)

Finally, if you want to pass a value through a sequence of functions, you can always do something like the following in clojure:

(defn pipeline [x & fns]   ((apply comp fns) x))  (pipeline 1 inc inc inc inc) => 5 
like image 30
mikera Avatar answered Sep 30 '22 12:09

mikera