Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: Why a function should be `declare` if it is called before definition in the source code

Tags:

clojure

In Clojure, if you call a function before its definition, e.g.

(foo (bar 'a))

(defn bar [] ...)

it is not compiled. One should add

(declare bar)

before (foo (bar 'a)). Why Clojure is designed as this? I mean, in most languages, except C/C++, such as Java, Python, PHP, Scala, Haskell or even other Lisps, especially in dynamic-type languages, function declaration is not needed, that is, function definition could be put either before or after a call. I feel it uncomfortable to use.

like image 325
seven7e Avatar asked Oct 15 '15 08:10

seven7e


People also ask

Is it necessary to declare function before use?

It is always recommended to declare a function before its use so that we don't see any surprises when the program is run (See this for more details).

How do you define a function in Clojure?

In Clojure, a function is defined using the defn keyword, followed by the function's name.

What is defn in Clojure?

defn defines a named function: ;; name params body ;; ----- ------ ------------------- (defn greet [name] (str "Hello, " name) ) This function has a single parameter name , however you may include any number of parameters in the params vector.

How do you return a function in Clojure?

Functions Returning Functions and Closures Our first function will be called adder . It will take a number, x , as its only argument and return a function. The function returned by adder will also take a single number, a , as its argument and return x + a . The returned function form adder is a closure.


1 Answers

Clojure does a single-pass compilation (well I simplify, read the following links) :

  • https://news.ycombinator.com/item?id=2467359
  • https://news.ycombinator.com/item?id=2466912

So it seems logical that if you read the source only one time, from top to bottom you cannot have things like forward declaration and do it safely.

To quote Rich (first link) :

But, what should happen here, when the compiler has never before seen bar?

`(defn foo [] (bar))`

or in CL:

`(defun foo () (bar))`

CL happily compiles it, and if bar is never defined, a runtime error will occur. Ok, but, what reified thing (symbol) did it use for bar during compilation? The symbol it interned when the form was read. So, what happens when you get the runtime error and realize that bar is defined in another package you forgot to import. You try to import other-package and, BAM!, another error - conflict, other-package:bar conflicts with read-in-package:bar. Then you go learn about uninterning.

In Clojure, the form doesn't compile, you get a message, and no var is interned for bar. You require other-namespace and continue.

I vastly prefer this experience, and so made these tradeoffs. Many other benefits came about from using a non-interning reader, and interning only on definition/declaration. I'm not inclined to give them up, nor the benefits mentioned earlier, in order to support circular reference.

like image 116
nha Avatar answered Oct 19 '22 09:10

nha