The short answer is that Clojure was designed to use a very simple one-pass compiler that simultaneously reads and compiles a single s-expression or form. For better or worse, there is no global type information, no global type inference, and no global analysis or optimization. Clojure uses instances of clojure.lang.Var
to create global bindings through a series of hash maps from text characters to transactional values. def
forms all the bindings in the global scope in this global binding map. So, where in Scala a โfunctionโ (method) will be allowed for an instance or static method for a given JVM class, in Clojure a โfunctionโ (def) is actually just a reference to an entry in the variable binding table. When a function is called, there is no static reference to another class, instead var is a reference by symbolic name, and then dereferenced to get an instance of the clojure.lang.IFn
object, which is then called.
This layer of indirection means that you can reevaluate only one definition at a time, and this reevaluation becomes global visible to all clients of the redefined var.
In comparison, when the definition in Scala changes, scalac must reload the modified file, macro, type infer, type checking, and compilation. Then, due to the semantics of loading classes on the JVM, scalac must also reload all classes that depend on the methods in the class that has changed. Also, all values โโthat are instances of the changed class become garbage.
Both approaches have their strengths and weaknesses. Obviously, the Clojure approach is easier to implement, however, it pays a constant cost in terms of performance due to the constant function search operations, forgetting about the correctness problems due to the lack of static types and what you have. This may be suitable for contexts in which many changes occur at short intervals (interactive development), but less suitable for contexts where the code is mostly static (deployment, therefore, Oxcart). some work that I did suggests that the slowdown in Clojure programs due to the lack of binding of the static method is about 16-25%. This does not mean that Clojure slow or Scala is fast, they just have different priorities.
Scala decides to do more work from the front, so that the compiled application will work better, which may be more suitable for deploying the application with a slight reboot or without rebooting, but will prove drag and drop if you want to make a lot of small changes.
Some of the materials I have on hand about compiling Clojure code is more or less cronological in publication order, since Nicholas influenced my GSoC work.
Which, I suppose, leaves me in an unfortunate place, simply saying: "Sorry, Scala was not intended for this, as Clojure was" in relation to hot-swapping code.