5

I started playing with Clojure today and stumbled upon the statement that one could change functions dynamically during runtime. That sounds pretty cool so I wrote a little piece of code using this feature.

(defn ^:dynamic state [x]
   (odd x))

(defn even [x]
  (if (= x 0)
    (println "even")
    (binding [state odd] (parity x))))

(defn odd [x]
  (if (= x 0)
    (println "odd")
    (binding [state even](parity x))))

(defn parity [x]
    (state (dec x)))

It works out fine, but since I am completly new to Clojure I don't know whether this is
a) clean functional code (since odd and even seem to have sideeffects?)
b) the way changing functions on runtime is supposed to be done

I would appreciate any kind of advice on that! :) -Zakum

2 Answers 2

5

Use of dynamic bindings is mostly a question of taste, but there are a few considerations:

Dynamic bindings are pretty much a shortcut for explicitly passing values on the call stack. There are only a few situations where doing that is a totally obvious win; mostly things like passing "global" configuration settings/arguments "through" APIs that don't support them.

An API that relies on dynamic bindings is hard to wrap into something more explicit, while the other way around is much easier (and can usually be done semi-automatically).

Dynamic bindings do not play nice with lazy sequences or anything else that evaluates outside of the current call stack (like other threads).

All in all, I think the "cleaner" functional solution would be to pass state as an argument to parity, but arguments can be made either way.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the insights, Joost! So did I understand you right, what I did is no sacrilege? I am aware of the fact, that one could write this functionality differently. But did I at least capture the idea of dynamic binding in Clojure right?
Personally, I would try and use explicit passing of values before going with dynamic bindings, mostly because of the disadvantages of bindings I mentioned, but it can make the code clearer and easier.
3

While being able to dynamically bind a symbol to different functions, I guess what you're after is really redefining a function.

Think of it this way: your code creates a symbol and two functions, and you dynamically bind the symbol to a different function:

                                   +---> func1
                                  /
symbol ---- [dynamic binding] ---<
                                  \
                                   +---> func2

The effect of your dynamic binding is limited to the scope of the binding invocation.

What we want to achieve is that, given a symbol and a function, provide a new implementation for the function so that all the code that refers to it will access the new implementation:

(defn func1 [...])

(var func1) ; ---> func1

(defn func1 [...])

(var func1) ; ---> func1*

and such a change permanently affects all the code that uses func1. This a normal task when you're developing a piece of clojure: you'll most likely have a REPL opened on a running application, and you'll def and defn several time the same symbols over and over again, redefining all the moving parts of your application on the fly.

If you're using Emacs and SLIME/Swank, any time you hit C-c C-k on a modified Clojure source file, you're potentially redefining all the functions in a namespace without the need to restart the application.

2 Comments

I don't really think that is what I ment (I'm not sure, though ^^). What you tell me is that I can write a function, use it, redefine it, use it again, and so on, right? For me, that is static, since the changes are made manually by the programmer. I am interested in writing functions that change other functions during runtime. A more practical example would be adding logging to an existing function. An function that changes itself would be a more peculiar example.
AFAIK dynamic variables in Clojure operate on Var bindings not on symbols.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.