Take the 2-minute tour ×

I find I very rarely use let in Clojure. For some reason I took a dislike to it when I started learning and have avoided using it ever since. It feels like the flow has stopped when let comes along. I was wondering, do you think we could do without it altogether ?

share|improve this question
Maybe you'll find this article interesting which covers using functions instead of let to explain monads. –  Dominic Kexel Nov 29 '12 at 15:24
thanks - very interesting –  Hendekagon Nov 29 '12 at 22:54
add comment

5 Answers

up vote 11 down vote accepted

You can replace any occurrence of (let [a1 b1 a2 b2...] ...) by ((fn [a1 a2 ...] ...) b1 b2 ...) so yes, we could. I am using let a lot though, and I'd rather not do without it.

share|improve this answer
In fact (let [a1 b1 a2 b2] ...) is better explained by (((fn [a1] (fn [a2] ...)) b1) b2). What I mean is that you can refer to previous bindings in subsequent ones. Edit: parentheses, we all love them. –  Jan Nov 28 '12 at 23:52
@Jan I guess, but that would have meant that I'd have had to write more parentheses. We can't have that now, can we? –  Cubic Nov 28 '12 at 23:54
this is a pattern I tend to use a lot & I love it –  Hendekagon Nov 29 '12 at 1:24
in fact, I think this answer gets to the problem: maybe every use of let is an opportunity to factor out another function ? maybe it's a sign that there's too much complexity in that part of the code ? –  Hendekagon Nov 29 '12 at 5:56
@Hendekagon Certainly you could refactor every single let in your code away. I tend to use it for very localized situations though, and I'd rather clutter up one function a bit than clutter up the rest of the program with one line functions I really only need in one place. –  Cubic Nov 29 '12 at 6:55
add comment

Let offers a few benefits. First, it allows value binding in a functional context. Second, it confers readability benefits. So while technically, one could do away with it (in the sense that you could still program without it), the language would be impoverished without a valuable tool.

One of the nice things about let is that it helps formalize a common (mathematical) way of specifying a computation, in which you introduce convenient bindings and then a simplified formula as a result. It's clear the bindings only apply to that "scope" and it's tie in with a more mathematical formulation is useful, especially for more functional programmers.

It's not a coincidence that let blocks occur in other languages like Haskell.

share|improve this answer
add comment

Let is indispensable to me in preventing multiple execution in macros:

(defmacro print-and-run [s-exp]
   `(do (println "running " (quote ~s-exp) "produced " ~s-exp)

would run s-exp twice, which is not what we want:

(defmacro print-and-run [s-exp]
  `(let [result# s-exp]
    (do (println "running " (quote s-exp) "produced " result#)

fixes this by binding the result of the expression to a name and referring to that result twice.

because the macro is returning an expression that will become part of another expression (macros are function that produce s-expressions) they need to produce local bindings to prevent multiple execution and avoide symbol capture.

share|improve this answer
for this kind of situation I tend to add multiple alternative argument lists to my function: (defn f ([x] (f x (dosomthing x))) ([x y] ...), don't know if that's possible with a macro though as I've never written a macro! –  Hendekagon Nov 29 '12 at 6:09
editing to include more on this –  Arthur Ulfeldt Nov 29 '12 at 20:33
add comment

I think I understand your question. Correct me if it's wrong. Some times "let" is used for imperative programming style. For example,

... (let [x (...)
          y (...x...)
          z (...x...y...)
          ....x...y...z...] ...

This pattern comes from imperative languages:

... { x = ...;
      y = ...x...;
      ...x...y...;} ...

You avoid this style and that's why you also avoid "let", don't you?

In some problems imperative style reduces amount of code. Furthermore, some times It's more efficient to write in java or c. Also in some cases "let" just holds values of subexpressions regardless of evaluation order. For example,

(... (let [a (...)
           b (...)...]
        (...a...b...a...b...) ;; still fp style
share|improve this answer
I think you've hit the nail on the head –  Hendekagon Nov 29 '12 at 5:52
Thanks. It would be nice if you'll mark the answer as correct ;) –  mobyte Nov 29 '12 at 7:19
I feel like you are saying that stuff like (let [a1 b1 a2 b2] (someexpression a1 a2)) was somewhat imperative. I would not agree with that. Since let is somewhat equivalent of (let [a1 b1] ...) by ((fn [a1] ...) b1) (see cubics answer) it should be clear that let is a functional concept. You seem to invoke the "serial evalutation" definition of "imperative" programming, but let is as serial invoking as nested function calls are. Of course I see the point that people new to FP tend to have a let-heavy style. –  wirrbel Apr 4 '13 at 6:20
add comment

There are at least two important use cases for let-bindings:

First, using let properly can make your code clearer and shorter. If you have an expression that you use more than once, binding it in a let is very nice. Here's a portion of the standard function map that uses let:

(let [s1 (seq c1) s2 (seq c2)]
  (when (and s1 s2)
    (cons (f (first s1) (first s2))
          (map f (rest s1) (rest s2)))))))

Even if you use an expression only once, it can still be helpful (to future readers of the code) to give it a semantically meaningful name.

Second, as Arthur mentioned, if you want to use the value of an expression more than once, but only want it evaluated once, you can't simply type out the entire expression twice: you need some kind of binding. This would be merely wasteful if you have a pure expression:

user=> (* (+ 3 2) (+ 3 2))

but actually changes the meaning of the program if the expression has side-effects:

user=> (* (+ 3 (do (println "hi") 2)) 
          (+ 3 (do (println "hi") 2)))

user=> (let [x (+ 3 (do (println "hi") 2))] 
         (* x x))                      
share|improve this answer
add comment

Your Answer


