let and Local Bindings
Prerequisites: Functions
let names values locally, inside an expression:
(defn describe-order [price quantity]
(let [subtotal (* price quantity)
tax (* subtotal 0.2)
total (+ subtotal tax)]
(format "subtotal %.2f, tax %.2f, total %.2f" subtotal tax total)))
(println (describe-order 4.5 3))
subtotal 13.50, tax 2.70, total 16.20
- Bindings come in pairs
[name value ...], evaluated in order — each can use the ones before it. - The names exist only inside the
letbody. letnames are not variables: you can't change them. You can shadow a name with a new binding of the same name, but the old value is unchanged.
Scope and shadowing
(def x 1)
(let [x 10
y (* x 2)]
(println "inside:" x y)
(let [x 100]
(println "nested:" x)))
(println "outside:" x)
inside: 10 20
nested: 100
outside: 1
The innermost binding wins; when a let ends, its names disappear.
Closures
(defn make-multiplier [factor]
(fn [value] (* value factor)))
(def triple (make-multiplier 3))
(println (triple 7))
(println (map (make-multiplier 10) [1 2 3]))
21
(10 20 30)
A function created inside another remembers the local values it can see
(factor), even after the outer function returned — a closure.
Practice
Write bmi that takes a weight in kg and a height in cm and returns a
string with the BMI to one decimal, using let for the intermediate
values.
Show solution
(defn bmi [kg cm]
(let [m (/ cm 100.0)
value (/ kg (* m m))]
(format "BMI: %.1f" value)))
(println (bmi 70 175))
BMI: 22.9