1
votes

I'm learning Clojure and am playing around with dynamic scope. I've discovered a few things:

  • Adding {:dynamic true} metadata to an existing Var doesn't actually make the Var dynamic. I can kind of see why you'd want this to be the case, but it can be misleading.
  • You can see if a Var is actually dynamic with (.isDynamic #'whatever)
  • You can (but probably shouldn't?) change whether a Var is dynamic with (.setDynamic #'whatever)

In short, it looks like the dynamic nature of a Var is stored in the guts of the clojure.lang.Var class itself, and setting ^:dynamic metadata at def time only will affect that internal state. Changing metadata afterward doesn't appear to affect it. OK, fine.

I discovered this when I was looking at the dynamic variables in namespace clojure.core. I checked to see what Vars in that namespace have :dynamic metadata set:

user=> (def metadyn (map key (filter #(-> % key resolve meta :dynamic) (ns-publics 'clojure.core))))
#'user/metadyn
user=> metadyn
(*3 *2 *print-level* *data-readers* *e *print-length* *1 *verbose-defrecords*
*clojure-version* *default-data-reader-fn* pr)

Rather fewer than I expected, missing things like *out* and *in*.

I checked to see what Vars actually are dynamic:

user=> (def realdyn (map key (filter #(-> % key resolve .isDynamic) (ns-publics 'clojure.core))))
#'user/realdyn
user=> realdyn
(*compile-path* *3 *2 *ns* *print-level* *allow-unresolved-vars* *data-readers*
*unchecked-math* *math-context* *read-eval* *compile-files* *command-line-args*
*warn-on-reflection* *e *flush-on-newline* *out* *print-length* *1 *file* *verbose-defrecords*
*clojure-version* *use-context-classloader* *err* *default-data-reader-fn* *agent* pr
*print-dup* *print-readably* *fn-loader* *print-meta* *in* *source-path* *compiler-options* *assert*)

Lots more.

So, the following Vars are dynamic but don't claim to be in their metadata:

user=> (clojure.set/difference (set realdyn) (set metadyn))
#{*compile-path* *ns* *allow-unresolved-vars* *unchecked-math* *math-context* *read-eval*
*compile-files* *command-line-args* *warn-on-reflection* *flush-on-newline* *out* *file*
*use-context-classloader* *err* *agent* *print-dup* *print-readably* *fn-loader* *print-meta*
*in* *source-path* *compiler-options* *assert*}

My question is simply: is there any significance to this, something I'm missing? Or, is it just a case of sloppy bookkeeping in the Clojure implementation?

No practical use for this; just trying to Understand.

1
They are created in the runtime before def is available.A. Webb
Thanks, good enough for me. If you put it in a reply, I'll mark it as the answer.Aron

1 Answers

1
votes

The dynamic vars you have identified without metadata are those created in the Java runtime (see RT.java starting around line 180). The others are created in Clojure after the bootstrapping has made def available (i.e. within core.clj and core_print.clj).

Metadata could have been added later and in some cases was (e.g. *agent* has a docstring), so this is not a complete answer. I could speculate that lack of practical importance, as you point out, has made truing-up the metadata of the vars used in bootstrapping a low priority.