I like my code to have a "top-down" structure, and that means I want to do exactly the opposite from what is natural in Clojure: functions being defined before they are used. This shouldn't be a problem, though, because I could theoretically declare
all my functions first, and just go on and enjoy life. But it seems in practice declare
cannot solve every single problem, and I would like to understand what is exactly the reason the following code does not work.
I have two functions, and I want to define a third by composing the two. The following three pieces of code accomplish this:
1
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(defn mycomp [x] (f (g x)))
(println (mycomp 10))
2
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(def mycomp (comp f g))
3
(declare f g)
(defn mycomp [x] (f (g x)))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
But what I would really like to write is
(declare f g)
(def mycomp (comp f g))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
And that gives me
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'user/g,
That would mean forward declaring works for many situations, but there are still some cases I can't just declare
all my functions and write the code in any way and in whatever order I like. What is the reason for this error? What does forward declaring really allows me to do, and what are the situations I must have the function already defined, such as for using comp
in this case? How can I tell when the definition is strictly necessary?
You can accomplish your goal if you take advantage of Clojure's (poorly documented) var
behavior:
(declare f g)
(def mycomp (comp #'f #'g))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(mycomp 10) => 45
Note that the syntax #'f
is just shorthand (technically a "reader macro") that translates into (var f)
. So you could write this directly:
(def mycomp (comp (var f) (var g)))
and get the same result.
Please see this answer for a more detailed answer on the (mostly hidden) interaction between a Clojure symbol, such as f
, and the (anonymous) Clojure var that the symbol points to, namely either #'f
or (var f)
. The var, in turn, then points to a value (such as your function (fn [x] (* x 3))
.
When you write an expression like (f 10)
, there is a 2-step indirection at work. First, the symbol f
is "evaluated" to find the associated var, then the var is "evaluated" to find the associated function. Most Clojure users are not really aware that this 2-step process exists, and nearly all of the time we can pretend that there is a direct connection between the symbol f
and the function value (fn [x] (* x 3))
.
The specific reason your original code doesn't work is that
(declare f g)
creates 2 "empty" vars. Just as (def x)
creates an association between the symbol x
and an empty var, that is what your declare
does. Thus, when the comp
function tries to extract the values from f
and g
, there is nothing present: the vars exist but they are empty.
P.S.
There is an exception to the above. If you have a let
form or similar, there is no var
involved:
(let [x 5
y (* 2 x) ]
y)
;=> 10
In the let
form, there is no var present. Instead, the compiler makes a direct connection between a symbol and its associated value; i.e. x => 5
and y => 10
.