Problems
- Better parity with JVM Clojure
- async Javascript (ie. nearly all Javascript) makes the binding macro effectively useless without bound-fn
- Some common Clojure behaviors, like printing to a dynamically bound *out*, can't currently exist across an async boundary. See *print-fn* in CLJS
- I'd like to create a dom manipulation library that has a *dom* dynamic variable, which acts like current working directory for dom manipulation functions. I've got some crazy ideas that I want to experiment with here, especially once I implement add-watch: I think I can achieve pretty seamless UI data binding.
Design
- Are Vars still useful without threads?
- Yes, but only dynamic vars
- async callbacks and threads have a lot of common design considerations
- Performance
- Price is equivalent to that of JVM Clojure
- Extra indirection for def and deref
- Shared stack of dynamic binding frames
- Hash lookup on each access
- Opt-in price for the Var indirection
- Treat ^:dynamic as that opt-in mechanism; no impact for static vars
- Potential optimization: Leverage Javascript's prototypical inheritance instead of Frame type
- Required compiler analysis
- Metadata for resolved vars
- Not available for external libraries
- OK, because we only care about ^:dynamic vars
- Interop
- Vars declared as ^:dynamic differ from static ones: they are wrapped in the Var type
- binding, etc are only applicable to dynamic vars, not useful to non-ClojureScript callers
- External callers can still use deref and alter-var-root
- Impact
- Breaking change for binding macro
- New behavior matches JVM clojure: binding only works on dynamic vars
- Simply marking any affected vars as ^:dynamic should be enough to upgrade
- Potentially breaking changes for any cljs.core vars that are changed to ^:dynamic
- *print-fn* and other printing vars
- Only breaking for Javascript interop usages, still source compatible
Labels: