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.