Skip to content

Typed Python return boundary

@compute returns a Plasm value. Monty owns expression semantics; Plasm resolves annotations, seals recursive output contracts and validates values before exposing them to the DAG. Returning data never grants entity or effect authority.

Annotation Contract
bool, int, float, str, None Builtin scalar/null; int is signed 64-bit, float finite
vN Exact session domain, constraints, catalog identity and hash
Row Actual input row shape; in a multi-input return, the inferred returned record shape, including observed presence
Value[eN] Declared entity data fields, without methods or traversal authority
T.field Field contract of a record type, e.g. Row.price
list[T] Recursive array value
T | U Union; T | None preserves T with nullability

A Program compute method may omit its return annotation. Monty infers through the complete body against its typed inputs, preserving field domains and named record fields. In a multi-input return, Row refers to the inferred returned record, not the dependency packet or whichever input was declared first. The authored annotation remains checked; list[Row] cannot return scalars. For example:

class Summary(Program):
    @compute
    def describe(self, rows: list[Row]):
        return [{"key": row.id, "title": row.title} for row in rows]

    def build(self):
        return self.describe(e1.query())

Bare dict and list[dict] require an inferred structural field contract; they do not authorize untyped output. A nested def in build is a scoped callback, not a compute method; declare @compute on the class and call self.method.

These forms compose recursively. A record can be returned as an input record or constructed as a matching Python dictionary. A field absent from an observed record stays absent; it is not manufactured as None. Python objects outside this value algebra, tuples, sets and dictionaries with non-string keys are rejected. JSON domains admit JSON integers through u64 without float conversion; the named integer domain remains i64. Cycles/deep graphs, nonfinite numbers and oversized outputs fail within bounded decoding. Domains resolve independently against their own pinned catalogs in a federated session; catalog graphs are not merged.

Compute results are consumed directly. Scalars are Python scalar values, arrays are array values, and records expose their declared fields. A per-row compute retains its input cardinality; a collection compute emits one value. content is an ordinary record field name, never a reserved accessor. Arrays do not acquire entity identity. Completeness, cancellation and reviewed effect rules are unchanged.

Consuming Python

class Adjust(Program):
    @compute
    def increment(self, row: Row) -> Row.count:
        return row.count + 1

    def build(self):
        source = e1.get("000123")
        result = self.increment(source.select("count"))
        return result

Row.count retains the source's named integer domain. A served vN can explicitly select a destination domain; its constraints are checked on the returned value. For writes, pass the singleton scalar result directly to the served mutation parameter, or select a declared field from a record result. An invalid return fails before any dependent mutation is dispatched. Previously completed, unrelated effects retain their existing failure semantics.

A scalar parameter requires both a scalar-cell contract and a proven singleton. A per-row scalar compute over plural input rejects at admission; use a bounded map for effects on each selected row. Corrections must not teach a magic .content accessor or the retired path-expression/heredoc surface. compute_values_have_no_reserved_content_accessor and plural_compute_values_cannot_fill_scalar_parameters lock these boundaries.

Executable evidence

The BC-03 typed_returns property shares the exhaustive CGS FieldType inventory with primitive_reductions: 20 domain fields × forward/reverse/empty inputs = 60 live programs. Each passes a nullable named-domain result through two Monty nodes, checks exact values and output contracts, complete coverage and absence of effects. The count case also rejects a forged output-domain contract during dry validation.

typed_return_structures_and_effect_gate additionally covers record pass-through, dictionary construction, arrays of records, unions, nested arrays/records, observed absent versus null fields, cross-catalog constrained domains and local annotations, unknown annotations, static return mismatches, valid mutation arguments and invalid output rejection before mutation IO. Codec unit tests cover lossless large integers, unsupported representations, nonfinite values and budgets.

The pinned Monty profile seals this boundary. Earlier text-only reviewed computes are not silently replayed under these rules. Text rendering remains the ordinary str case.

Outer expressions

Outer conditional unions and arithmetic now follow the outer value transfer contract. Selection preserves complete branch domains; arithmetic produces storage types without unproved constraints. First/last reductions retain their input contracts. Arbitrary constructor/type/effect products remain explicit matrix obligations.