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.