compile/define-parameter and compile/parameterq

Syntax: (define-parameter NAME [DEFAULT]), (parameterq NAME [DEFAULT])

Behaviour: define-parameter binds NAME to a ParamRef, a re-resolving accessor: every read and store finds the innermost parameterize binding of the context it runs in, and the root scope of the current domain when there is none. parameterq reads a parameter by name without a binding, through parameter.

A default declares, it does not store. It is the answer a read gives when nothing is bound, and it belongs to the ref or the read site that names it, so two sites can declare different defaults without racing. A read of an unbound name with no default raises NameError.

A value lives in the enclosing parameterize extent first, then in the root scope. parameterize binds in the running context’s own table, so concurrent tasks never see each other’s bindings. A store with no parameterize in the way lands in the root scope, shared by every call on the thread: the channel for host pre-defines and for values set once at startup. A defer awaited directly runs under the extents applied at the await. A task created from it reads the bindings live at its creation. The root scope is the thread’s either way.

Root scope: Koskari code called from Python shares one root parameter scope per thread. The scope is created on first use and lives as long as the thread, so a store made with no parameterize in the way during one call is read by every later call on that thread, through any function, and by Python through a ParamRef. Another thread starts with a root scope of its own and never sees these stores. A coroutine or defer runs under the root scope of the thread that resumes it, wherever it was created; only its parameterize bindings travel with it. Inside a with Domain(): block the block’s Domain supplies the root scope instead, for Python reads and for Koskari calls made inside the block; after the block, the thread’s scope answers again with its earlier stores. 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 a block can only be closed by the stack that opened it.

Examples:

(define-parameter verbose False)

def show [] (if verbose "loud" "quiet")

(show)                                    ;; "quiet"
(parameterize [[verbose True]] (show))    ;; "loud"
(parameterq verbose False)                ;; False
>>> (macroexpand-1 '(define-parameter verbose False))
(begin (var verbose) (bindq verbose (ParamRef (quote verbose) False)))

See also: parameterize, ref for (ref (parameter NAME)).