Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can clojure be made fully dynamic?

Tags:

clojure

In clojure 1.1, all calls were dynamic, meaning that you could redefine a function at the REPL and it would be included in the running program automatically. This was also nice for things like dotrace.

In clojure 1.2, many calls seem to be statically linked, and if I want to replace a function, Sometimes, I have to find all the places where it's called and put #' in front of them.

Worse, I can't predict where I'll need to do this.

Is it possible to go back to the old default of dynamic linking? Maybe if you needed the extra iota of speed you could switch it back on for the production app, but for development I much prefer the 1.1 behaviour.

I'm hoping for some sort of compiler option like *warn-on-reflection*.

Edit:

I'm confused about what's going on. More specifically, here are two functions. I prefer the behaviour of the second. How can I make the first one behave like the second, as I believe it used to do in 1.1?

user> (clojure-version)
"1.2.0"

user> (defn factorial[n] (if (< n 2) n (* n (factorial (dec n)))))
#'user/factorial

user> (require 'clojure.contrib.trace)
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1670: (factorial 10)
TRACE t1670: => 3628800

user> (defn factorial[n] (if (< n 2) n (* n (#'factorial (dec n)))))
#'user/factorial
user> (clojure.contrib.trace/dotrace (factorial) (factorial 10))
TRACE t1681: (factorial 10)
TRACE t1682: |    (factorial 9)
TRACE t1683: |    |    (factorial 8)
TRACE t1684: |    |    |    (factorial 7)
TRACE t1685: |    |    |    |    (factorial 6)
TRACE t1686: |    |    |    |    |    (factorial 5)
TRACE t1687: |    |    |    |    |    |    (factorial 4)
TRACE t1688: |    |    |    |    |    |    |    (factorial 3)
TRACE t1689: |    |    |    |    |    |    |    |    (factorial 2)
TRACE t1690: |    |    |    |    |    |    |    |    |    (factorial 1)
TRACE t1690: |    |    |    |    |    |    |    |    |    => 1
TRACE t1689: |    |    |    |    |    |    |    |    => 2
TRACE t1688: |    |    |    |    |    |    |    => 6
TRACE t1687: |    |    |    |    |    |    => 24
TRACE t1686: |    |    |    |    |    => 120
TRACE t1685: |    |    |    |    => 720
TRACE t1684: |    |    |    => 5040
TRACE t1683: |    |    => 40320
TRACE t1682: |    => 362880
TRACE t1681: => 3628800
3628800

Edit (to the whole question, and a change of title):

Joost points out below that what's actually going on here is that the self call in factorial is being optimized away. I can't see why that would be done, since you can't do that many recursive self calls without blowing the stack, but it explains the observed behaviour. Perhaps it's something to do with anonymous self-calls.

The original reason for my question was that I was trying to write http://www.learningclojure.com/2011/03/hello-web-dynamic-compojure-web.html, and I got irritated with the number of places i had to type #' to get the behaviour I expected. That and the dotrace made me think that the general dynamic behaviour had gone and that the on the fly redefining, which works in some places, must be done with some clever hack.

In retrospect that seems a strange conclusion to jump to, but now I'm just confused (which is better!). Are there any references for all this? I'd love to have a general theory of when this will work and when it won't.

like image 789
John Lawrence Aspden Avatar asked Mar 22 '11 22:03

John Lawrence Aspden


People also ask

Is Clojure dynamically-typed?

While Clojure is a dynamically-typed language, it's possible to supplement types by using the spec library. It allows to validate simple types, enumerations, maps and collections, just as with any statically-typed language.

Is Clojure interpreted or compiled?

Clojure is strictly a compiled language, just like Java and Scala.

Is Clojure functional?

Clojure is a dialect of Lisp, and shares with Lisp the code-as-data philosophy and a powerful macro system. Clojure is predominantly a functional programming language, and features a rich set of immutable, persistent data structures.

Should I learn Clojure or go?

The fact was, Go is so much easier to learn than C, and does very nearly match C's performance! Learning Go was much easier for me than learning Clojure. Go being an imperative language, allowed me a slightly more familiar, and less steep learning curve than the functional paradigm of Clojure.


2 Answers

Everything in Clojure is fully dynamic, but you have to take note of when you're working with a Var and when you're working with the Function which is the current value of that Var.

In your first example:

(defn factorial [n] (if (< n 2) n (* n (factorial (dec n)))))

The factorial symbol is resolved to the Var #'user/factorial, which is then evaluated by the compiler to get its current value, a compiled function. This evaluation happens only once, when the function is compiled. The factorial in this first example is the value of the Var #'user/factorial at the moment the function was defined.

In the second example:

(defn factorial [n] (if (< n 2) n (* n (#'factorial (dec n)))))

You have explicitly asked for the Var #'user/factorial. Invoking a Var has the same effect as dereferencing the Var and invoking its (function) value. This example could be written more explicitly as:

(defn factorial [n] (if (< n 2) n (* n ((deref (var factorial)) (dec n)))))

The clojure.contrib.trace/dotrace macro (which I wrote, ages ago) uses binding to temporarily rebind a Var to a different value. It does not change the definition of any functions. Instead, it creates a new function which calls the original function and prints the trace lines, then it binds that function to the Var.

In your first example, since the original function was compiled with the value of the factorial function, dotrace has no effect. In the second example, each invocation of the factorial function looks up the current value of the #'user/factorial Var, so each invocation sees the alternate binding created by dotrace.

like image 128
Stuart Sierra Avatar answered Dec 05 '22 17:12

Stuart Sierra


So that people don't confused about the issues, I'm explaining the "problem" related to web development.

This is a limitation of Ring not Clojure (and really it's a limitation of the Java Jetty library). You can always redefine functions normally. However the handler given to the Jetty server process cannot be redefined. Your functions are being updated, but the Jetty server cannot see these updates. Providing a var as the handler is the work around in this case.

But note the var is not the real handler. An AbstractHandler must be given to the Jetty server, so Ring uses proxy to create one which closes over your handler. This is why in order for the handler to updated dynamically it needs to be var and not a fn.

like image 44
dnolen Avatar answered Dec 05 '22 15:12

dnolen