idea[havogt]: ambient values bound at execution time (closure-variable stretch goal) - #29
idea[havogt]: ambient values bound at execution time (closure-variable stretch goal)#29havogt wants to merge 3 commits into
Conversation
…e stretch goal Records the ContextVar variant next to the read-only-field stretch goal it extends: a globally declared value bound at program-execution time, so mesh properties (Cartesian scalars, unstructured connectivities and weights) need not be threaded through signatures. Late binding is what lets one set of operators run against a second mesh in a process.
…goal Records what the gt4py branch established: a declaration carries its own type so no type-system change is needed; the reference becomes a synthesised program parameter added once at definition; and the only difference between the two forms is whether that parameter is static. Adds the measured variant counts and the three things the design turned out not to need.
|
Updated with what the prototype (havogt/gt4py#71) actually established. The section is now marked prototyped, with measured claims separated from open ones. The mechanism turned out simpler than the original text assumed. A declaration carries its own type ( That makes the two forms differ in exactly one place: whether the parameter is listed as static. Three things the design does not need, each assumed at some point:
Binding time is restated. "Static for the lifetime of the application" is the wrong unit; the useful one is static for a jitted program. That turns the two forms into a declaration-site choice of what identifies a value in the jit key, which also dissolves the apparent conflict with the JAX connectivities proposal (#25): a connectivity wants descriptor-identity, a scalar wants value-identity, and nothing forces one policy on both. Flagged as open, most importantly: synthesised parameters currently take the closure variable's local name, so two operators naming the same declaration differently — or two modules that both use |
…ansition Records that connectivities and values now bind by one rule — the declaration is the key — which retires the name-harvesting 'Namespace' and its collision check. Splits the transition: moving the binding surface is done and touches nothing below it; making connectivities ordinary ambient values is a separate question, since the offset provider's type drives domain inference and connectivity typing.
Records an idea next to the proposal it actually belongs to, rather than as a
separate document:
closure-variable-resolutionalready has a"Stretch goal: closing over read-only fields" appendix, and this is the same
mechanism with a later binding time.
The idea
A user declares a value once, globally, and any field operator reaches it
without it appearing in a signature — a
ContextVarfilled atprogram-execution time (JIT time for compiled backends).
The motivating case is mesh properties: scalars on a Cartesian grid (
dx,dy)and fields on an unstructured one (connectivities plus their weights). Both are
fixed for the lifetime of the application, and both must today be carried
explicitly — as extra operator parameters, or as the
offset_providerthreadeddown the call chain. Every operator between the caller and the one that actually
needs a weight has to name it.
Why it is mostly unification, not invention
ContextVars are already how gt4py carries such values:next/embedded/context.pyholds_offset_providerand_closure_column_range, with anupdate()context manager — internal andembedded-only today.
ConstantPythonNamespaceObject = eve_utils.FrozenNamespace | enum.EnumMeta(
type_translation.py), folded inclosure_var_folding.py, fingerprint-awarein
fingerprinting.py.__gt_implicit_args__(), and bind the binding, not the buffer, so only thetype/domain descriptor enters the compiled cache key.
What late binding adds over the decoration-time form is that the same operators
can run against a different mesh in one process; binding at decoration time
fixes the value before any operator is defined.
Open questions recorded, not answered
What "static for the lifetime of the application" means operationally (per
process, per
ContextVarscope, or per compiled program — and what happens onrebinding); which part of an ambient field enters the fingerprint; how an ambient
declaration is typed, and whether an operator's type should reflect its ambient
dependencies at all; and the standard objection to dynamic scoping — errors when
nothing is bound must be good, and there must be a way to see what an operator
depends on ambiently.
The ceiling it aims at is a mesh concept on top (linked to
mesh-and-first-class-halos), and it is a plausible substrate fordiscretization-independent-fd-syntax, whose mesh-invariant surface needsweights and connectivity to come from a declared mesh property rather than from
operator arguments.
Index keywords updated to match the extended
tags; no new index entry, sincethis is an appendix section rather than a proposal of its own.