Skip to end of metadata
Go to start of metadata

Problems

  1. Want better parity with JVM Clojure
  2. async Javascript (ie. nearly all Javascript) makes the binding macro effectively useless without bound-fn
  3. Some common Clojure behaviors, like printing to a dynamically bound *out*, can't currently exist across an async boundary. See *print-fn* in CLJS
  4. 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! Async callbacks and threads have a lot of common design considerations
    • Several interactive development use cases
      • Vars are invokable, so you can preserve the indirection with the #'var special form
        • (comp f #'g) ; returns a function that dynamically updates with g, but not with f.
      • IWatchable can be used to instantly re-test a function in a live browser
  • 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:
  1. Feb 19, 2012

    It seems that I am unable to edit this wiki.... I just wanted to add some more notes:

    I've realized that Vars which satisfy IWatchable are useful even if they are not dynamic. This is true during development, where you can set a watch on a var to automatically run some code when a function is re-defined via the repl. In particular, I've got a defview macro which causes a subset of the dom to re-render with the updated view function. I'm doubtful that dynamic vars would be useful after advanced compilation, but am not totally sure.

    Given their usefulness, there needs to be a better mechanism for getting a Var than to use ^:dynamic. Maybe simply ^:var, where ^{:var false, :dynamic true} would be disallowed.

    Regarding the breaking change to the binding macro, it's pre-var behavior matches the intended behavior of JVM Clojure's with-redefs macro.

    My current work in progress of implementing Vars can be found here: https://github.com/brandonbloom/clojurescript/compare/8ba4849e60e5957cdac36ef6946c647e824ca3c8...vars

    Lots more discussion here: https://groups.google.com/d/topic/clojure/6cmnkHmHBNw/discussion

  2. Apr 25, 2012

    I updated my notes above with the desirability of static Vars, but I need to do a little bit more thinking about the implementation. I think we can get static Var objects without needing the extra indirection, which means we can bypass the indirection for them. The trick would be to implement the var special form as a memoizing map lookup. This way, the cost is moved to calls to (var foo) which is infrequent. Dynamic vars still pay the indirection cost.

    I'm working up a patch now.