Are clojure function cyclic dependencies specifically disallowed by design, or is it just a reader behaviour?
Asked Answered
E

2

7

If I do the following in clojure

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

I get the following error:

java.lang.Exception: Unable to resolve symbol: sub1b in this context

But if I do the following:

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (- a 1)))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (- a 1)))

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

It runs just fine.

Is this by design, or just a function of the way the Clojure reader works?

Enchilada answered 8/8, 2010 at 10:29 Comment(0)
L
17

You can do

(declare sub1a sub1b)

'declare' is specifically meant to create a var with no bindings for making forward declarations.

One you declared the names:

(defn sub1a [a]
  (cond
    (= a 0) 0
    true (sub1b (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    true (sub1a (- a 1) )))

(println (sub1a 10))

Also the idomatic way to specify the default condition in cond (for clojure) is using the :else clause. This is a bit different from Common Lisp which uses T (for True). So your previous code can be rewritten as:

(defn sub1a [a]
  (cond
    (= a 0) 0
    :else (sub1b (- a 1) )))

...
Laciniate answered 8/8, 2010 at 10:53 Comment(0)
S
3

The correct solution is as posted by rkrishnan.

As for this part of the question:

Is this by design, or just a function of the way the Clojure reader works?

Actually this is nothing to do with the Clojure reader -- it's because the compiler resolves symbols to Vars immediately upon encountering them (in those positions where they would need "ultimately" to be resolved to a Var, as opposed to places where they name locals, are quoted or passed to a special form or a macro, of course). This makes sense for reasons of efficiency: knowing which Var a symbol refers to at compile time makes it possible to generate code which needs not resolve symbols at runtime (it still normally needs to look up the values of the Vars, but not the Vars themselves). If you really wanted to, you could have your code resolve symbols at runtime:

(defn sub1a [a]
  (cond
    (= a 0) 0
    :else ((resolve 'sub1b) (- a 1) )))

(defn sub1b [a]
  (cond
    (= a 0) 0
    :else ((resolve 'sub1a) (- a 1) )))

(println (sub1a 10))

; prints 0 and returns nil

This does, however, cause a certain degradation in performance which is hardly ever justified in real code, thus Clojure makes you be explicit about it if you really think this is what you want.

Sheeran answered 8/8, 2010 at 18:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.