Skip to content

Expressions, comparison and evaluation order

Choose output shape from the required behavior. Tuple comparison evaluates its operands differently from a sequence of short-circuit field comparisons. A passing integer equality example does not establish callback or raw-result semantics.

For a field-wise short-circuit algorithm, the intended Python sketch is:

result = left.first == right.first
if not result:
    return result
return left.last == right.last

This sketch preserves a falsey non-bool intermediate object and returns the final comparison result without an additional truth conversion. Actual resources must bind direct read paths, preserve deliberate cross-scope locals and order the contributions. Empty operands require an explicit consumer policy. Python minor versions can change the dataclass reference algorithm; do not assume one shape matches every minor.

Status of the recorded nested chain

The inventory's nested indexed expression arrangement failed with term_path, next_term and native-materialization symptoms. Its general cause remains unresolved. A flat ordered alternative passed a bounded Dataclass differential corpus; this does not prove the nested mechanism broken or the alternative universally equivalent. No new nested-chain support is claimed here.

Bare statement-form astichi_ref is value-only and was rejected in the recorded case. The consumer's static expression carrier (read if True else None) is a bounded observed alternative. Its precise backend and overhead properties must be qualified separately; astichi_funcargs is not automatically a scalar tuple carrier.

Use collection/port ordering and explicit build indices; do not rely on fact insertion for semantic evaluation order. Before promoting a new expression recipe, check falsey non-bool results, the final raw object, nonreflexive/shared operands, getter traces and callback counts. Consumer excerpts link the qualified comparison authority. Compatibility keeps unresolved cases visible.

Executable flat three-operand recipe

This new standalone recipe assembles two guarded intermediate comparisons and one final direct return. It checks falsey non-bool identity, callback order, truth-conversion counts, a shared/nonreflexive input and zero operands. It is a bounded algorithm of its own, not an assertion of dataclass equivalence across Python minors. It does not explain or fix the original D17 nested chain.

Source authority: example/documentation/comparison.py.

"""A bounded three-operand short-circuit recipe; not a general dataclass algorithm."""

from __future__ import annotations

import ast
from dataclasses import dataclass

from yidl.concept_parser import compile_yidl_files
from yidl.generation.data_def_sys import emit_concept_runtime_source

SOURCE = """
concept Comparison {
    property Name: str
    property Order: int
    property Last: bool
    record Operand { Name Order Last }
    collection Operands: Operand identity Name many
    resource Root = code $[
        def compare(left, right):
            astichi_hole(steps)
            return True
    ]$ { keep compare }
    resource Step = template $[
        result = astichi_pass(left, outer_bind=True).astichi_ref(external=path) == astichi_pass(right, outer_bind=True).astichi_ref(external=path)
        if not result:
            return result
    ]$
    resource Final = template $[
        return astichi_pass(left, outer_bind=True).astichi_ref(external=path) == astichi_pass(right, outer_bind=True).astichi_ref(external=path)
    ]$
    resource Empty = template `pass`
    contribution Next = Step {
        as Operand index Order order Order target steps { build /Root }
        external path = Name
    }
    contribution LastOperand = Final {
        as Operand index Order order Order target steps { build /Root }
        external path = Name
    }
    contribution EmptyOperand = Empty {
        as Empty target steps { build /Root }
    }
    matcher EmptySelection() -> contribution { default -> EmptyOperand }
    matcher Select(operand: Operands) -> contribution {
        default -> Next
        rule final when Last == True -> LastOperand
    }
    production RootProduction -> composable {
        root Root = Root
        apply empty using EmptySelection
        apply steps from operand: Operands using Select
    }
    assembly Module = RootProduction
}
"""


@dataclass
class Raw:
    truth: bool
    conversions: list[bool]

    def __bool__(self) -> bool:
        self.conversions.append(self.truth)
        return self.truth


@dataclass(frozen=True, eq=False)
class Value:
    name: str
    result: Raw
    events: list[str]

    def __eq__(self, other: object) -> object:
        self.events.append(self.name)
        return self.result


@dataclass
class Pair:
    first: object
    middle: object
    last: object


def run() -> str:
    concept = compile_yidl_files(
        {"comparison.yidl": SOURCE}, "comparison.yidl"
    ).concepts["Comparison"]
    runtime = emit_concept_runtime_source(
        concept.plan.build_data_definition(),
        resources=concept.resources,
        assembly_plan=concept,
    )
    schema: dict[str, object] = {}
    exec(runtime, schema)
    builder = schema["new_builder"]()
    for name, order, last in (
        ("last", 30, True),
        ("first", 10, False),
        ("middle", 20, False),
    ):
        builder.add(
            schema["OperandsCollection"],
            schema["Operand"](name=name, order=order, last=last),
        )
    tree = schema["build_Module"](builder.freeze()).to_executable_ast()
    namespace: dict[str, object] = {}
    exec(compile(tree, "comparison_model.py", "exec"), namespace)
    compare = namespace["compare"]
    events: list[str] = []
    yes, no, final = Raw(True, []), Raw(False, []), Raw(False, [])
    left = Pair(
        Value("first", no, events),
        Value("middle", yes, events),
        Value("last", final, events),
    )
    right = Pair(object(), object(), object())
    assert compare(left, right) is no
    assert events == ["first"] and no.conversions == [False]
    events.clear()
    left.first = Value("first", yes, events)
    assert compare(left, right) is final
    assert events == ["first", "middle", "last"]
    assert yes.conversions == [True, True] and final.conversions == []
    events.clear()
    # There is no implicit identity shortcut for shared/nonreflexive values.
    assert compare(left, left) is final
    assert events == ["first", "middle", "last"]
    empty_tree = schema["build_Module"](
        schema["new_builder"]().freeze()
    ).to_executable_ast()
    empty_namespace: dict[str, object] = {}
    exec(compile(empty_tree, "empty_comparison.py", "exec"), empty_namespace)
    assert empty_namespace["compare"](left, right) is True
    return ast.unparse(tree) + "\n"


if __name__ == "__main__":
    print(run(), end="")

Source authority: tests/data/goldens/materialized/documentation_classes/comparison_model.py.

def compare(left, right):
    pass
    result = left.first == right.first
    if not result:
        return result
    result__astichi_scoped_1 = left.middle == right.middle
    if not result__astichi_scoped_1:
        return result__astichi_scoped_1
    return left.last == right.last
    return True