Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does ^:dynamic do in Clojure?

Tags:

clojure

I've googled for "clojure dynamic" and "clojure dynamic scope" and read over 10 articles and I still don't have a clear understanding of what ^:dyanmic does. I think this article may be answering my question, but the code samples seem to be "missing" so I'm not even sure if it's referring to the same thing I'm confused about.

I'm trying to fix an issue in the clj-http project but first I have to understand the code. It has functions defined like this:

(defn ^:dynamic parse-html
  "Resolve and apply crouton's HTML parsing."
  [& args]
  {:pre [crouton-enabled?]}
  (apply (ns-resolve (symbol "crouton.html") (symbol "parse")) args))

But I don't understand what that ^:dynamic means/does. Can anyone explain it to me in a really simple way?

like image 573
Daniel Kaplan Avatar asked Apr 01 '13 02:04

Daniel Kaplan


People also ask

What is binding in Clojure?

binding => var-symbol init-expr Creates new bindings for the (already-existing) vars, with the supplied initial values, executes the exprs in an implicit do, then re-establishes the bindings that existed before.

What is a VAR in Clojure?

In Clojure, variables are defined by the 'def' keyword. It's a bit different wherein the concept of variables has more to do with binding. In Clojure, a value is bound to a variable.

Is Clojure interpreted or compiled?

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

What is Clojure REPL?

A Clojure REPL (standing for Read-Eval-Print Loop) is a programming environment which enables the programmer to interact with a running Clojure program and modify it, by evaluating one code expression at a time.


1 Answers

It is defining the function as being dynamically scoped.

In other words, this allows someone to re-bind parse-html in a given function invocation and have that new binding apply only to functions called from that specific invocation.

If parse-html were not dynamically scoped then re-binding it would cause the new binding to be seen by any code that uses parse-html and not just code that was activated by the function invocation that did the re-binding.

Dynamic scoping is useful as a substitute for globally scoped variables. A function can say "let current_numeric_base = 16; call other functions;" and the other functions will all print in hexadecimal. Then when they return, and the base-setting function returns, the base will return to whatever it was. http://c2.com/cgi/wiki?DynamicScoping


As was pointed out in the comments below, you can't actually re-bind variables that aren't dynamically scoped in Clojure. If you could, updating a variable that is lexically scoped will impact all code that is executed even if it is running in a different call stack from where the re-bind occurred.

So maybe some pseudo-code will make the difference between dynamic and lexical scope clear.

Example of using a dynamically scoped variable:

(def ^:dynamic a 0)

(defn some-func [x] (+ x 1))

; re-binds a to 1 for everything in the callstack from the (binding)
; call and down
(binding [a 1] 
   (print (some-func a)))

 ; a was only re-bound for anything that was called from
 ; within binding (above) so at this point a is bound to 0.
(print (some-func a))

would print: 2 1

Example of a lexically scoped variable:

(def a 0)

(defn some-func [x] (+ x 1))

; re-binds a to 1 for everyone, not just things that 
; are in the callstack created at this line
(set-var [a 1]  ; set-var is a made up function that can re-bind lexically scoped variables
   (print (some-func a)))

; a was lexically scoped so changing it changed
; it globally and not just for the callstack that
; contained the set-var.
(print (some-func a))

would print: 2 2

like image 155
Matt Wonlaw Avatar answered Sep 19 '22 13:09

Matt Wonlaw