Clojure compiletime vs runtime

275 views
Skip to first unread message

Andrew Chambers

unread,
Apr 15, 2014, 12:39:59 AM4/15/14
to clo...@googlegroups.com
Is there an explanation of how clojure deals with scoping and its static checking. It seems to be a hybrid of a static language and a dynamic language when it comes to compilation. I'll elaborate.

The following code wont compile:
(defn x [] nil)
(defn y[]) ((x))

however this code will compile:

(defn foo[] (defn x[] nil))
(defn y[]) ((x))

but calling y before foo fails with a runtime exception.

Also, the following code:

(println "hello")
(defn -main [args]
  (println "world"))

prints "hello" at compile time
and also
"hello
world" at runtime.

My conclusions from this is that the static symbol checker is actually fairly stupid and is just there to provide some simple sanity, and that all toplevel code in a namespace
is executed at compile time AND at runtime. Is this correct?

Andrew Chambers

unread,
Apr 15, 2014, 12:41:49 AM4/15/14
to clo...@googlegroups.com
Forgive me, the first example was meant to be
The following code wont compile:
(defn y[]) ((x))
(defn x [] nil)

Softaddicts

unread,
Apr 15, 2014, 5:04:43 AM4/15/14
to clo...@googlegroups.com
Ahem :)

a) The fn x does not exist in the universe until you call foo, hence you cannot
expect the compiler to known anything about it if it's not called before
making any reference to x.

b) If you refer to x at the top level (the "universe" above :) before defining
it, obviously it does not exist. You might use (declare x) before
referring to it. This allows you to define it later at your convenience.

c) In general, you should avoid using def/defn within a function.
Macros may do that but this is a different story.

d) Yes code at the top level is executed, how can you expect
the REPL to work interactively if forms are not evaluated first ?
A defn expands to a function call, a special form in fact but it still behaves
like a function. Any function call at top level will get executed after being
compiled to byte code.

Now about the "compilation" step...

Traditionally, most Lisps allow you to refer to stuff in interpreted mode
not defined yet hoping that it will get defined by the time you run the code
that refers to these undefined things. It can even be something transient on
the stack... oups...

You can still compile in other Lisps but this is a special case where you have to
make sure that stuff is defined in some way. You need to add directives to
tell the compiler that this stuff will exist later and that it can safely refer to it.

On the JVM, some underlying byte code has to be generated for any forms
typed in the REPL at the top level any reference has to be defined before hand.

There's no other way to generate byte code... there cannot be black holes
waiting to get filled later.

Hence the "compilation" phase and the restriction
that stuff you refer to are to be defined either directly or using declare.

Hope it explains the compromise.

Luc P.
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
--
Softaddicts<lprefo...@softaddicts.ca> sent by ibisMail from my ipad!

Andrew Chambers

unread,
Apr 15, 2014, 6:36:43 AM4/15/14
to clo...@googlegroups.com
Why are the toplevel forms which arent macros executed at compile time? For example Lua can be compiled to bytecode without executing
its top level calls.


You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/VUWTAwOEHS0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Phillip Lord

unread,
Apr 15, 2014, 7:05:01 AM4/15/14
to clo...@googlegroups.com

You need to distinguish between "compiled" and "aot compiled to byte
code".

As far as I know, all forms are compiled before they are executed. So,
if you type:

(+ 1 1)

it is first compiled to bytecode, and then run. It's not executed at
compile time at all; rather when it is evaluated, it is compiled and
then run. If you aot compile, then you are do the same thing, but dump
the bytecode to file. So, you still evaluate the entire top level.

So, when you say compile, I presume you you mean aot compiled. Macros
have to be run, so the question is should they be expanded but not
evaluated? And what about macros with side-effects?

And if top level forms are NOT evaluated when loaded what would this do:

(ns user)
(def x 1)

(load "user_y")

=== in user_y.clj
(def y 1)


Now the AOT would only load the first file in the namespace because
(load "user_y") would not be evaluated.

Why are you worried about this? Most of the time compilation in Clojure
is an implementation detail, as it is in python. It just happens when it
needs to, and away you go.

Phil
--
Phillip Lord, Phone: +44 (0) 191 222 7827
Lecturer in Bioinformatics, Email: philli...@newcastle.ac.uk
School of Computing Science, http://homepages.cs.ncl.ac.uk/phillip.lord
Room 914 Claremont Tower, skype: russet_apples
Newcastle University, twitter: phillord
NE1 7RU

Andrew Chambers

unread,
Apr 15, 2014, 7:13:29 AM4/15/14
to clo...@googlegroups.com
I only noticed it because I was trying to write a macro which expands to multiple def calls. This requires the def's to be inside a do block, which made me question a whole lot about how the AOT compiler works.

Phillip Lord

unread,
Apr 15, 2014, 8:07:38 AM4/15/14
to clo...@googlegroups.com

Ah, yeah, that. I had that problem. Consider this...

user> (do (def x 1) x)
1

user> (do (intern *ns* 'y 1) y)
1

user> (defn dointernw [val]
(def w val) w)
#'user/dointernw

user> (defn dointernz[val]
(intern *ns* 'z val) z)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: z in this context, compiling:(/tmp/form-init5921558438713284713.clj:1:1)


So, why does the (do (intern form succeed while the dointernz fail. The
answer is that, sneakily, the do form is compiled specially.
Essentially,

(do (intern *ns* 'y 1) y) is compiled as two statements

(intern *ns* 'y 1)
y

So, the intern is compiled and run, and everything is cool.


But, here, the whole function is compiled first, and so z hasn't been
defined yet.

(defn dointernz[val]
(intern *ns* 'z val) z)

This broken my code a while back. I wanted to test some "def*" forms,
which I had defined using a macro with an intern. So

(is (defblah x 1) x)

seems a reasonable test to see if defblah is working. But with this
definition of defblah the test won't compile.

(defmacro defblah [symb val]
`(intern *ns* ~symb ~val))

while with this definition it will.

(defmacro defblah [symb val]
`(def ~symb ~val))

So, yes, do is handled specially. It's not at all obvious, and I lost
several days to this.

But this is not an issue of AOT compilation, just compilation.

Luc Prefontaine

unread,
Apr 15, 2014, 8:21:34 AM4/15/14
to clo...@googlegroups.com
Compilation is mandatory before
executing anything.

By default when code is loaded it's
executed. That's how a Lisp behaves.

If you want to isolate compilation,
from execution, you can use AOT (ahead of time
compilation).

You would use this to
deliver compiled code w/o source
code, make sure there's no missing
references, ...

But this is not used in interactive
development where you want to
redefine stuff, change states, ....
and be as dynamic as possible.

Do you have a specific need that
you want to cover ?

Luc P.
Luc Prefontaine<lprefo...@softaddicts.ca> sent by ibisMail!
Reply all
Reply to author
Forward
0 new messages