Error formatting macro: pagetree: java.lang.NullPointerException

Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.


  • dynamic binding *scope* holds collection of things needing cleanup
  • with-open binds a new *scope*, cleans up *scope* at end
  • lib APIs can call a new fn (scope thing) to add a resource to the current bound scope
    • throws exception if no scope available
  • REPL cleans up scope before every loop
  • *scope* is an atom, so N "child" threads can add to it
    • binding conveyance makes this work
    • still up to you to make sure "parent" thread outlives children

IMO this nails all the use cases and problems above, with a few
caveats that I think can be subsumed into a single example as follows:


  • investigate interaction with fork/join
    • think this reduces to "make binding conveyance work with fork/join"
  • new protocol or IFn for bits of cleanup logic placed in a scope
    • handling Closeable and IFn in an if statement for now
    • code in core happens before protocols are available
  • verify that change to with-open is non-breaking for existing code
    • but note no benefit either, until you start calling (scope ...)


  1. Creating a resource whose lifetime is bounded to yours: use with-open, as you do today
  2. Creating a resource that will be passed back to a caller unconsumed: call (scope res)
    1. caller must have made a scope
    2. mandatory scope is not a limitation here, it is a sensible requirement
  3. Returning >1 resources from a function with different lifecycles and cleanup rules: not supported
    1. not going to worry about this without realistic example
  4. Create resources from child threads
    1. works if thread creation conveys bindings
    2. you are responsible for waiting on child threads
  5. Consuming one resource, pass another through to caller: put with-open around the one you are consuming
    Code Block
    ;; a cleaned up here, b cleaned up by caller
    (let [a (with-open [a ...] ...
          b (scope ...)]
  6. Decide on the fly whether to clean up or let parent do it
    Code Block
    ;; capture-scope? grab-scope? capture-cleanup?
    (let [[a s] (capture-scope (make-a))]
      (if (done? ...)
        (s) ;; cleanup myself
        (scope s) ;; punt