37
votes

I have a simple yet frustrating problem in Clojure, I have a function (let's call it read-function) which figures out what the user wants to do from his input then calls another function that does that (let's call it action-function). This action-function calls the read-function when it's done so the user can perform another task.

Now my problem is that if I put the code for read-function before the code for action-function, I get an error in read-function saying that it doesn't know what action-function is (because the code for it is further down) and if I do the opposite, well I get a similar error obviously, saying that read-function cannot be resolved etc.

Is there a simple way to fix this ?

The actual code:

(defn ajout [botin]
  (def botin botin)
  (readCmd botin)
)

(defn readCmd [botin]
  (println "Entrez une commande svp ")
  (def botin botin)
  (let [cmd (read-line)]
    (if (.equals cmd "a") ((println "Ajout 8o") (ajout botin))
      (if (.equals cmd "e") ((println "Elim 8o") (eliminer botin))
        (if (.equals cmd "i") ((println "Imprim 8o") (imprimer botin))
          ((println "Commande invalide, nous vous rapellons que les commandes possibles sont : ") (print-les-cmd) (readCmd))))))


)

like this, I get an error at the (readCmd botin) line in the ajout function saying : Unable to resolve symbol: readCmd in this context

If I put the code for these two functions in the reverse order I will get an error saying : Unable to resolve symbol: ajout in this context

4
Could you post a simple code sample of what isn't working. That'd help track down the problem.seth
Of course ----Code Start---- (defn add [book] (def book-in-function book) (readCmd book-in-function) ) (defn readCmd [book] (println "Enter cmd") (def book-in-function book) (let [cmd (read-line)] (if (.equals cmd "add") ((println "Adding") (add book-in-function)) (println "Dont know")) ) ) ---Code End--- If I put it like this, I get Unable to resolve symbol: readCmd in this context at the line of the read-cmd call in the add function If I put the two functions in the reverse order I get a similar error but for the add function in the read-cmd functionJoOb
I have edited the original post after seeing what the comment looked like :)JoOb
Upvoted, because I know people will be googling for this in the future! Good question!Rayne

4 Answers

64
votes

You can use forward declarations in Clojure so you can call functions that haven't been defined yet.

(declare readCmd)

should work!

In Clojure, the order in which you define functions is important, a function can't call another function (or anything for that matter) that hasn't been defined yet. That's why we have forward declarations.

18
votes

As the others already answered, you need to (declare readCmd) to fix your immediate problem.

However, there are still problems with this code, because it actually implements iterative process using mutual recursion (readCmd -> ajout -> readCmd -> imprimer -> readCmd -> ...) which will consume stack and will you'll get (on) stack overflow. A better way to organize this, would be to make readCmd tail recursive, and make it call the actions. When an action returns, readCmd tail recursively calls itself.

Also this code snippet:

((println "Ajout 8o") (ajout botin))

probably is not what you want to do: it'll call println and will try to use the result as a function. Use "do" instead:

(do (println "Ajout 8o") (ajout botin))

You may also consider reading about case or cond, they will simplify the nested ifs.

Another strange thing about your code is

(def botin botin)

what it is about?

11
votes

At the top of your code put:

(declare readCmd)
2
votes

There's a thread on the Clojure google group about this that provides some interesting considerations, in particular on how the use of declare can trip up some tools in the ecosystem:

thread

Of course, you could argue good tools should work with all the constructs of the language :)

IMO, you kind of get used to the bottom-up style and just read in reverse. It's a bit of a different story your telling where you build things up rather than decompose them.

And of course, as others have said, you can forward declare with

(declare my-function)