Skip to content

Bind names and assignment targets

The tutorial uses three distinct bindings for one direct store:

astichi_pass(self, outer_bind=True).astichi_ref(external=slot)._ = astichi_pass(value__astichi_arg__, outer_bind=True)

self and the bound parameter are reads of the containing constructor scope. slot is an external path string such as x, not a Python object value. The trailing ._ makes the reference a transparent Store target. The output is self.x = x; the marker calls do not run when the user constructs the object.

An ordinary authored name target plus identifier binding is enough for simple local assignments. A naked value expression is not automatically a Store target. Python rejects Load-context AST used on the left of assignment. Transparent ._ / .astichi_v wrappers exist for supported assignment, augmented-assignment and deletion surfaces; each carrier/context/backend needs qualification.

Scope markers

Marker Role
astichi_keep(name) / suffix keep Preserve identifier spelling through hygiene
astichi_import(name) Declaration importing a parent-scope binding
astichi_export(name) Publish a local binding for wiring
astichi_pass(name, outer_bind=True) Value reference across a scope boundary
name__astichi_arg__ Identifier demand resolved during binding
astichi_ref(external=path) Expression-only Name/Attribute path

A keep does not install a capture object. A parameter demand does not rename an attribute just because it has a similar spelling. An outer read does not acquire target context without the correct carrier. Use dotted identifier paths for nested attributes, not arbitrary source expressions in a path string.

When a binding survives as a marker, inspect its exact AST position and remaining demands. If an external is “already queued,” check whether two live insertions share a build identity. The tutorial's composite numeric field indices avoid collisions across owners; collection FieldId alone did not set those indices.