Can Clojure do without obstacles?

I find that I rarely use let in Clojure. For some reason, I did not like this when I started to study and since then have avoided using it. It looks like the thread stopped when let arrives. I was wondering, do you think that we could do without this?

+6
source share
6 answers

You can replace any event (let [a1 b1 a2 b2...] ...) with ((fn [a1 a2 ...] ...) b1 b2 ...) , so yes, we could. I use, albeit a lot, and I would prefer not to do without it.

+12
source

Allows you to offer several advantages. First, it allows you to bind value in a functional context. Secondly, it provides readability benefits. Thus, while technically, one could end this (in the sense that you could program without it), the language would be impoverished without a valuable tool.

One of the nice things is that it helps to formalize the general (mathematical) way of specifying calculations, in which you enter convenient bindings, and then the simplified formula as a result. It’s clear that the bindings apply only to this “scope”, and it is associated with a more mathematical formulation that is useful, especially for more functional programmers.

It is no coincidence that let the blocks occur in other languages ​​such as Haskell.

+12
source

For me, it is mandatory to prevent multiple execution in macros:

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

will 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#) result#)) 

corrects this by associating the result of an expression with a name and referencing this result twice.

because a macro returns an expression that will become part of another expression (macros are functions that produce s-expressions), they need to create local bindings to prevent multiple execution and to avoid character capture .

+6
source

I think I understand your question. Correct me if it is wrong. Several times, let is used for an imperative programming style. For instance,

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

This template comes from imperative languages:

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

You avoid this style and why you also avoid the "let", right?

In some problems, an imperative style reduces the amount of code. In addition, several times it is more efficient to write in java or c. Also in some cases, let simply stores the values ​​of the subexpressions regardless of the order of evaluation. For instance,

 (... (let [a (...) b (...)...] (...a...b...a...b...) ;; still fp style 
+5
source

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

First, using let , you can make the code clearer and shorter. If you have an expression that you use more than once, binding it in let very nice. Here is the part of the standard map function 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 be useful (for future code readers) to give it a semantically meaningful name.

Secondly, as Arthur said, if you want to use the value of an expression more than once, but only want it to be evaluated once, you cannot just print the entire expression twice: you need some kind of binding. That would be simply wasteful if you had a pure expression:

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

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

 user=> (* (+ 3 (do (println "hi") 2)) (+ 3 (do (println "hi") 2))) hi hi 25 user=> (let [x (+ 3 (do (println "hi") 2))] (* xx)) hi 25 
+2
source

I came across this recently, so some timings:

 (testing "Repeat vs Let vs Fn" (let [start (System/currentTimeMillis)] (dotimes [x 1000000] (* (+ 3 2) (+ 3 2))) (prn (- (System/currentTimeMillis) start))) (let [start (System/currentTimeMillis) n (+ 3 2)] (dotimes [x 1000000] (* nn)) (prn (- (System/currentTimeMillis) start))) (let [start (System/currentTimeMillis)] (dotimes [x 1000000] ((fn [x] (* xx)) (+ 3 2))) (prn (- (System/currentTimeMillis) start))))) Output Testing Repeat vs Let vs Fn 116 18 60 

'let' wins over the "pure" functionality.

0
source

All Articles