Lesser known Clojure: letfn form


Sometimes it’s useful to declare functions for a local scope only. Typically this can be achieved by using let form. Let’s look at such example:

(let [print-number (fn [n]
                     (println n))
      decrease (fn [n]
                 (when (> n 0)
                   (print-number (dec n))))]
  (decrease 10))

This form will decrease 10 and print the result:

=>
9

Let’s assume we need to modify print-number function, to call decrease after printing number. The result would be then:

=>
9
8
7
6
5
4
3
2
0

if we modify our let form in such way:

(let [print-number (fn [n]
                     (println n)
                     (decrease n))
      decrease (fn [n]
                 (when (> n 0)
                   (print-number (dec n))))]
  (decrease 10))

we will get compiler exception: Unable to resolve symbol: decrease in this context.
We get this error, because let form bounds symbols (print-number, decrease) to their respective expressions in the top down order. This means that decrease isn’t known when bounding print-number symbol. To solve this we can use letfn form!

First let’s look at the documentation:

clojure.core/letfn
  (letfn [fnspecs*] exprs*)
Special Form
  fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)

  Takes a vector of function specs and a body, and generates a set of
  bindings of functions to their names. All of the names are available
  in all of the definitions of the functions, as well as the body.

as you can read from this doc, letfn is very similar to the let form, with this difference, that declared symbols are available everywhere (not only in top down order) and it’s used only for functions. Now we can use letfn to write previous code in error free way:

(letfn [(print-number [n]
          (println n)
          (decrease n))
        (decrease [n]
          (when (> n 0)
            (print-number (dec n))))]
  (decrease 10))

this time decrease is available in print-number function. In many situation it can be very helpful to know and remember about this form.


You may also like

ClojureScript: JavaScript Interop

Lesser known Clojure: variants of threading macro

Template method pattern