Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind conditionally a dynamic var from Clojure's core for backwards compatibility

Tags:

clojure

I've been trying for a while to solve this nREPL ticket and I'm out of ideas, so I decided to ask for a bit of help here.

Basically we need to be bind *print-namespace-maps* only if it's present or find some way to define if it's not present that doesn't mess up Clojure 1.9+.

Here are some of the things that don't work:

;; compilation error
(when (resolve '*print-namespace-maps*)
  (set! *print-namespace-maps* (@bindings #'*print-namespace-maps*)))

;; shadows `clojure.core/*print-namespace-maps*` on 1.9, as the def gets executed always
(when-not (resolve 'clojure.core/*print-namespace-maps*)
  (def ^:dynamic *print-namespace-maps*
    "Define the var for clojure versions below 1.9"
    nil))

Seems that dynamic vars can't be conditionally bound at all, which really sucks, but I guess there must be some way to do something like what we're aiming for.

Take a look at the linked issue for more details. Any help would be much appreciated!

like image 391
Bozhidar Batsov Avatar asked Dec 02 '25 23:12

Bozhidar Batsov


1 Answers

I don't totally understand the details of the linked issue or nREPL impl., but the def behavior is a bit surprising here:

Clojure 1.8.0
user=> (when false (def ^:dynamic *foo* 1))
nil

I would've expected there to be no *foo* var after that evaluation, but it creates an unbound var:

user=> (var-get #'*foo*)
#object[clojure.lang.Var$Unbound 0x20da8800 "Unbound: #'user/*foo*"]

And I suppose that def compiler behavior is what causes the exception in 1.9 in your example:

WARNING: *print-namespace-maps* already refers to: #'clojure.core/*print-namespace-maps* in namespace: user, being replaced by: #'user/*print-namespace-maps*

Maybe a macro could be used so that the resolve check happens during macro expansion, and doesn't emit a def if it resolves:

(defmacro def-dynamic-when-not-resolve [sym value]
  (when-not (resolve sym)
    `(def ~(vary-meta sym assoc :dynamic true) ~value)))

It seems to work on Clojure 1.8:

Clojure 1.8.0
user=> (def-dynamic-when-not-resolve *print-namespace-maps* 'sure)
#'user/*print-namespace-maps*
user=> *print-namespace-maps*
sure

And on Clojure 1.9:

Clojure 1.9.0
user=> (def-dynamic-when-not-resolve *print-namespace-maps* 'sure)
nil
user=> *print-namespace-maps*
true
like image 134
Taylor Wood Avatar answered Dec 05 '25 15:12

Taylor Wood



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!