Skip to content
AgileLogics
4 min read

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 let body.
  • let names 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