Reputation: 818
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.
Upvotes: 15
Views: 3740
Reputation: 18005
Clojure does a single-pass compilation (well I simplify, read the following links) :
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.
Upvotes: 24