compile/parameterize

Symbol: compile/parameterize

Syntax: (parameterize BINDINGS BODY ...)

Bindings: Same as let: list of [SYM EXPR] or (EXPR as: SYM); parsed with gather_bindings.

Use-case: Temporarily rebind dynamic parameters for the duration of the body. Parameters are read by parameterq, by the parameter builtin, or through a ParamRef. When the body runs those reads see the parameterized values; when the block exits the previous bindings are restored. At least one binding is required.

Behaviour: Compiles each value expression onto the stack (first binding deepest, last at TOS), emits PUSH_PARAM with a constant names tuple, compiles the body with helper_begin, then emits POP_PARAM. Uses the VM’s PARAM extent; restore on exit (and on unwind) is handled by the evaluator.

Task isolation: A parameterize extent binds in the running context’s own table, and never in the shared root scope. A coroutine, defer, or task captures the currently-applied bindings when it is created (the defer is the call site; create-task is transport), and from then on its extents bind and restore in its own table. Two concurrently-driven tasks never observe or disturb each other’s bindings, even when they interleave across awaits, and a task abandoned mid-extent cannot leak its bindings anywhere. A set! on a parameter inside the extent writes the extent’s binding and is discarded with it when the block exits.

Root scope: A read that finds no parameterize binding falls through to the root scope, one per thread or per with Domain(): block; see define-parameter.

Parameterize from Python: koskari.types.parameterize opens the same extent over a block of Python code: with parameterize(width=5):, or with parameterize({"line-width": 5}): for a name that is not a Python identifier. Inside the block a read from Python or from Koskari code called within it sees the binding; a block opened by Python code that Koskari called out to is inner to the calling extent. A coroutine, task, or asyncio task created inside the block keeps the bindings applied at its creation. The block belongs to the running stack, a thread’s synchronous stack or one asyncio task, so blocks on different tasks never see each other and each exit restores only what that block bound. A store inside the block writes the block’s binding, not the root scope.

Examples:

;; Parameterize format; consumer pretty reads it via (parameterq format)
(parameterize [[format "woot %r woot"]]
  (pretty 100))

;; Multiple parameters
(parameterize [[a 100] [b 101]]
  (check_both))