Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Metaprogramming Patterns

Reactive State

The pattern behind Vue, MobX, and similar frameworks:

(func reactive
  :args (:object initial :function on-change)
  :returns :any
  :body
  (new Proxy initial (obj
    :set (fn (:any target :string prop :any value :any receiver)
      (bind old (Reflect:get target prop receiver))
      (Reflect:set target prop value receiver)
      (if (not (= old value)) (on-change prop value old))
      true))))

(bind state (reactive (obj :count 0)
  (fn (:string prop :any val :any old)
    (console:log (template prop ": " old " → " val)))))

Every property assignment triggers the callback. The UI framework uses this to re-render when state changes.

Immutable Wrapper

(func immutable
  :args (:object target)
  :returns :any
  :body
  (new Proxy target (obj
    :set (fn () (throw (new TypeError "Object is immutable")))
    :delete-property (fn () (throw (new TypeError "Object is immutable"))))))

Logging Wrapper

The simplest useful proxy — forward everything, log access:

(bind logged (new Proxy target (obj
  :get (fn (:any t :string p :any r)
    (console:log (template "get " p))
    (Reflect:get t p r)))))

Membrane Pattern

Wraps an entire object graph — any object returned from the proxy is also wrapped, recursively. The get trap checks whether the returned value is an object and, if so, wraps it in another proxy with the same handler. This ensures that traversing proxy:nested:deep passes through interception at every level.

Used for sandboxing and security boundaries — the caller can access the entire object graph but every operation is mediated. Mark Miller’s research on object capabilities provides the theoretical foundation; the TC39 Realms proposal builds on similar ideas for isolation between code contexts.