liveBook · Manning

Letfn

As explained in Clojure: The Essential Reference

letfn is a Clojure macro that binds locally scoped named functions whose names are mutually visible within the binding form.

Use `letfn` when a computation needs named helper functions that should remain local. Like a combination of `let` and `fn`, it binds functions within a body, but every bound function can refer to every other one—even functions named later in the binding vector. This makes mutual recursion possible without namespace-level definitions. section 3.1.3 section 2.1.1

The shape is `(letfn [(name [params] exprs...) ...] body...)`. Each function needs a name and a parameter vector, which may be empty and may use destructuring. A function with no expressions has an implicit `nil` body. `letfn` returns the result of its body, or `nil` when no body is supplied. section 3.1.3

Name a small local operation

`(letfn [(square [x] (* x x))] (map square (range 10)))` returns `(0 1 4 9 16 25 36 49 64 81)`. The name makes the mapping operation readable without extracting `square` into the namespace. section 3.1.3

Refer to a later function

`(letfn [(even? [n] (or (zero? n) #(odd? (dec n)))) (odd? [n] (and (not (zero? n)) #(even? (dec n))))] (trampoline odd? 120))` returns `false`. Both names are visible throughout the bindings; the example uses `trampoline` to run their mutually recursive calls. section 3.1.3

(defn locs-xform [match]
  (letfn [(matching? [ns]   ; #1
            (re-find
              (re-pattern match)
              (str (ns-name ns))))
          (var->sym [{:keys [ns name]}]
            (symbol (str ns) (str name)))
          (count-lines [fsym]
            (count
              (clojure.string/split-lines
                (or (clojure.repl/source-fn fsym) ""))))]
    (comp                   ; #2
      (filter matching?)
      (map ns-interns)
      (mapcat vals)
      (map meta)
      (map var->sym)
      (map (juxt identity count-lines)))))

A transducer can keep its main pipeline compact by naming the private operations that match namespaces, construct symbols, and count source lines. section 3.1.3

Choose `letfn` for helpers substantial enough to name but not general enough to expose. Unlike ordinary `let` bindings, its function names are mutually visible. Be careful when reusing a name in nested `letfn` forms: a self-reference can call the inner function rather than the outer one and cause a `StackOverflowError`. `letfn*` supplies the underlying special form; there is no particular reason to use it directly. section 3.1.3 section 3.1

How Clojure: The Essential Reference covers it

How deep it goes
Defines syntax, destructuring, return behavior and mutual visibility; defers detailed mutual recursion and does not develop letfn* internals.
Examples
Uses a square-mapping helper, mutually recursive parity tests, and named helpers in a transducer.
What it assumes
Assumes familiarity with Clojure functions and lexical bindings while specifying letfn’s syntax and return behavior.
What you can do after
Apply it via letfn to write named local helpers and mutually recursive functions.
How it connects
Relates letfn to lexical binding, let, fn, defn, mutual recursion, trampoline and transducers.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.