Reputation: 7679
I do not understand what kind of advantages or disadvantages are in using let
, because it is possible to avoid to use let at all like in the code below?
(defn make-adder [x]
(let [y x]
(fn [z] (+ y z))))
(def add2 (make-adder 2))
;; or without let
(defn add2 [z] (+ 2 z))
(add2 4)
Upvotes: 1
Views: 444
Reputation: 6033
In most programming languages, you can declare and use local variables that helps you in the design of the function (or method) body. In Clojure, let
let you bind local symbols to any expression that can result into a value, a data structure or another local function definition.
Your example is so simple that there is no need for the let
construct, but you will soon encounter cases where it will be of great help for the function logic readability.
let
is also a way to factorise code, which improve readability and performance by computing an expression only once and use the result in several places.
And, last but not least, let
destructuring is very convenient to produce concise code when expressions return compound structures like collections.
Upvotes: 7
Reputation: 13473
The issue is not whether to use let
, but whether to define your add2
function directly or use a function-making-function, make-adder
, to make it.
Your function-making-function is
(defn make-adder [x]
(let [y x]
(fn [z] (+ y z))))
As @user1571406 says, the let
does nothing useful here, and is better omitted, giving
(defn make-adder [x]
(fn [z] (+ x z)))
... which is much easier to read. We can see that, given a number, it returns a function that adds that number to whatever it is presented with:
((make-adder 2) 4)
;6
Giving (make-adder 2)
a name is just confusing.
Defining it explicitly, a nameless function that adds 2
is
(fn [z] (+ 2 z))
Applying it to 4
:
((fn [z] (+ 2 z)) 4)
;6
Upvotes: 2
Reputation: 3014
There's three main reasons to name intermediate results:
let
-ed name but not give it out to the outside world. In your make-adder
example, there's no real need for the let
because it's just establishing an alias for the incoming parameter. But if you have something a bit more involved then these advantages start becoming relevant.
Just because I have it to hand, here's some code from another recent answer of mine:
(defn trades-chan
"Open the URL as a tab-separated values stream of trades.
Returns a core.async channel of the trades, represented as maps.
Closes the HTTP stream on channel close!"
[dump-url]
(let[stream (-> dump-url
(client/get {:as :stream})
:body) ;;This is an example of 3.
lines (-> stream
io/reader
line-seq)
;; This is an example of 2. I don't want to make multiple readers just because I use them in multiple expressions.
;;fields and transducer are examples of 1.
fields (map keyword (str/split (first lines) #"\t"))
transducer (map (comp #(zipmap fields %) #(str/split % #"\t")))
;;output-chan is another example of 2
output-chan (async/chan 50 transducer)]
(async/go-loop [my-lines (drop 1 lines)]
(if (async/>! output-chan (first my-lines))
(recur (rest my-lines))
(.close stream))) ;;Here, the closure closes the HTTP stream, so it needs a name to refer to it by.
output-chan))
Upvotes: 7