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.
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).
In Clojure, a function is defined using the defn keyword, followed by the function's name.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With