Dynamically bind functions at runtime using Clojure

Today I started playing with Clojure and stumbled upon a statement that functions can be dynamically changed at runtime. This sounds pretty cool, so I wrote a small part of the code using this function.

(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))) 

This works fine, but since I am completely new to Clojure, I don’t know if it is a) pure functional code (since there are odd and even seeming side effects?)
b) a way to perform functions at runtime -

I would be grateful for any advice on this! :) -Zakum

+4
source share
2 answers

The use of dynamic bindings is mostly taste dependent, but there are a few considerations:

Dynamic bindings are largely shortcuts for explicitly passing values ​​in the call stack. There are only a few situations where this is an absolutely obvious victory; basically things like passing "global" configuration settings / arguments through APIs that don't support them.

An API that relies on dynamic bindings is hard to embed in something more explicit, but vice versa - much simpler (and usually can be done semi-automatically).

Dynamic bindings do not play well with lazy sequences or anything else that is evaluated outside of the current call stack (for example, other threads).

In general, I think a β€œpure” functional solution would be to pass state as an argument to parity , but arguments can be made anyway.

+4
source

During the dynamic binding of a symbol to various functions, I assume that you really redefine the function after that.

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

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

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

What we want to achieve is that, taking into account the symbol and function, provide a new implementation for the function so that all the code that refers to it refers to the new implementation:

 (defn func1 [...]) (var func1) ; ---> func1 (defn func1 [...]) (var func1) ; ---> func1* 

and such a change constantly affects all the code that func1 uses. This is a common task when you are developing a clojure piece: you will most likely have REPL open in the running application, and you will def and defn repeat the same characters over and over again, overriding all moving parts of your application on the fly .

If you use Emacs and SLIME / Swank, each time you press Cc Ck in a modified Clojure source file, you potentially override all functions in the namespace without having to restart the application.

+2
source

Source: https://habr.com/ru/post/1384125/


All Articles