JoOb
JoOb

Reputation: 989

Order of code in Clojure

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

Upvotes: 39

Views: 6430

Answers (4)

leeor
leeor

Reputation: 17751

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)

Upvotes: 3

Rayne
Rayne

Reputation: 32625

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.

Upvotes: 65

ivant
ivant

Reputation: 3919

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?

Upvotes: 17

Brian Carper
Brian Carper

Reputation: 72926

At the top of your code put:

(declare readCmd)

Upvotes: 11

Related Questions