Skip to main content

Sexp

Enum Sexp 

Source
pub enum Sexp {
    Nil,
    Atom(Atom),
    List(Vec<Sexp>),
    Quote(Box<Sexp>),
    Quasiquote(Box<Sexp>),
    Unquote(Box<Sexp>),
    UnquoteSplice(Box<Sexp>),
}

Variants§

§

Nil

§

Atom(Atom)

§

List(Vec<Sexp>)

§

Quote(Box<Sexp>)

'x — literal; does not participate in macro substitution.

§

Quasiquote(Box<Sexp>)

`x — quasi-quotation; substitution happens inside.

§

Unquote(Box<Sexp>)

,x — substitute the binding named x. Only valid inside a quasi-quote.

§

UnquoteSplice(Box<Sexp>)

,@x — splice the list x into the containing list.

Implementations§

Source§

impl Sexp

Source

pub const LIST_OPEN: char = '('

Canonical ( char that opens a Self::List rendering AND (paired with Self::LIST_CLOSE) the empty Self::Nil rendering (). Outer-structural peer of Atom::STR_DELIMITER on the atomic-payload delimiter axis: where STR_DELIMITER is the ONE " byte the reader’s tokenizer’s FOUR Token::Str-round-trip sites bind to on the closed-set Atom algebra, LIST_OPEN is the ONE ( byte the reader’s tokenizer’s Token::LParen outer-dispatch arm AND the bare-atom terminator disjunct AND [fmt::Display for Sexp]’s list-opening emission AND Self::Nil’s two-char () rendering all bind to on the closed-set outer Sexp algebra.

Pre-lift the same '(' byte lived inline at FOUR sites: two outer-match arms in crate::reader::tokenize (the Token::LParen construction arm AND the bare-atom terminator’s || ch == '(' disjunct), and two Display arms in [fmt::Display for Sexp] (the Self::List(_) opener AND the Self::Nil two-char () rendering’s left char). Post-lift the (typed structural role, canonical byte) pairing binds at ONE constant on the Sexp algebra that every consumer routes through; a refactor that swaps the byte (e.g. a Racket-style port to [ for square-bracket list literals, an S-expression-DSL port to { for brace-list syntax) touches ONE constant rather than four inline bytes that would silently drift out of round-trip agreement if one was updated without the others.

Load-bearing paired-delimiter contract: Sexp::LIST_OPEN MUST pair section-for-retraction with Self::LIST_CLOSE at every round-trip site — the reader’s Token::LParen (from LIST_OPEN) MUST be closed by a Token::RParen (from LIST_CLOSE) for a well-formed list, and the Display impl’s Self::List(_) arm MUST emit LIST_OPEN at the opener AND LIST_CLOSE at the closer for the reader-then-Display round trip parse(display(list)) == list to hold. Guards the paired disjointness across the closed-set outer Sexp algebra so a future refactor that renames one constant without updating the other fails at rustc / test time rather than as a silent list- rendering asymmetry.

Cross-axis disjointness with the sibling delimiters (pinned structurally at sexp_list_delimiters_distinct_from_every_other_algebra_marker): LIST_OPEN’s byte MUST differ from Atom::STR_DELIMITER ('"'), Atom::KEYWORD_MARKER (":"), the two Atom::bool_literal spellings ("#t" / "#f") AND every QuoteForm::lead_char projection ('\'', '`', ',') on the substrate’s outer-marker axes. Otherwise the reader’s Token::LParen outer-dispatch arm would ambiguously route through a sibling algebra’s arm.

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (Self::List outer structure, canonical ( opener) pairing now binds at ONE constant on the closed-set outer Sexp algebra regardless of which of the four consumer surfaces reaches in. THEORY.md §VI.1 — generation over composition; four byte-identical inline '(' char literals across two substrate files collapse onto ONE named constant, matching the substrate’s three-times rule. THEORY.md §V.1 — knowable platform; the canonical list-opener byte becomes a TYPE-level constant on the outer substrate algebra rather than four inline bytes at four consumer surfaces across two substrate files (crate::reader and crate::ast).

Source

pub const LIST_CLOSE: char = ')'

Canonical ) char that closes a Self::List rendering AND (paired with Self::LIST_OPEN) the empty Self::Nil rendering (). See Self::LIST_OPEN for the substrate-wide paired-delimiter contract, cross-axis disjointness, and theory anchors — this constant is its section-for-retraction sibling on the closer axis. Four consumer sites (the reader’s Token::RParen outer-dispatch arm, the bare-atom terminator’s || ch == ')' disjunct, [fmt::Display for Sexp]’s Self::List(_) closer, Self::Nil’s two-char () rendering’s right char) all bind here.

Source

pub const LIST_DELIMITERS: [char; 2]

Canonical paired list-delimiter closed-set ALL array — composes Self::LIST_OPEN followed by Self::LIST_CLOSE in canonical declaration order, forced-arity [char; 2] so a hypothetical third list-delimiter row would extend this array + one algebra constant in lockstep. Peer to Atom::SELF_ESCAPE_TABLE ([char; 2] on the Str-payload self-escape sub-vocabulary axis of the closed-set Atom algebra): where SELF_ESCAPE_TABLE closes the two pattern-EQUALS-value inner-tokenizer arms of Atom::decode_str_escape as ONE typed forced-arity array, LIST_DELIMITERS closes the two outer-structural list-delimiter arms of the reader’s outer-dispatch as the analogous typed forced-arity array one axis over on the closed-set outer Sexp algebra.

Pre-lift the two-element [Self::LIST_OPEN, Self::LIST_CLOSE] composition lived inline at TWO sites: the two || ch == Self::LIST_{OPEN,CLOSE} disjuncts inside Self::is_bare_atom_boundary’s reader-boundary projection (spanning TWO of the SIX categories the projection carries), AND the [Sexp::LIST_OPEN, Sexp::LIST_CLOSE].iter().collect() array literal at the Nil Display composition-pin test that binds the two-char () rendering to the two typed constants. Post-lift the sub-vocabulary sweep binds at ONE typed forced-arity array on the closed-set outer Sexp algebra rather than at two inline algebra-constant enumerations per consumer. Adding a hypothetical Racket-compat square-bracket list mode ([Self::LIST_OPEN, Self::LIST_CLOSE, Self::VEC_OPEN, Self::VEC_CLOSE]) would extend LIST_DELIMITERS ONCE + Self::is_bare_atom_boundary’s sub-vocabulary sweep ONCE + two new algebra constants (opener + closer) in lockstep; rustc’s forced-arity check on [char; N] binds the extension through the array declaration site.

Structural invariant carried at the SHAPE level: [char; 2] pairs section-for-retraction one-to-one with Atom::SELF_ESCAPE_TABLE’s [char; 2] — the two arrays sit on distinct closed-set algebras (outer-structural list-delimiter vocabulary on Sexp; inner-Str-payload self-escape vocabulary on Atom) but share the same forced-arity shape at their respective sub-vocabulary axes. A consumer that reaches for one of the two arrays encodes its vocabulary’s paired-role identity in the SHAPE it iterates rather than in a per-site convention.

Composition law (round-trip): LIST_DELIMITERS[0] == Self::LIST_OPEN AND LIST_DELIMITERS[1] == Self::LIST_CLOSE AND LIST_DELIMITERS.len() == 2. The forced-arity + canonical declaration order together pin every downstream index-sweep consumer to the (opener, closer) pairing at rustc time; a reorder without reordering the underlying algebra constants fails at the composition pin below.

Cross-axis disjointness pinned structurally at [sexp_list_delimiters_distinct_from_every_other_algebra_marker]: neither element aliases any sibling outer-marker char on the substrate’s other closed-set algebras — the Str-payload delimiter (Atom::STR_DELIMITER), the Keyword-marker prefix (Atom::KEYWORD_MARKER_LEAD), the Comment-lead byte (Self::COMMENT_LEAD), every quote-family lead char (QuoteForm::lead_char), and every Bool-literal spelling’s first char.

Theory anchor: THEORY.md §III — the typescape; the paired (opener, closer) list-delimiter sub-vocabulary becomes a typed forced-arity ALL array on the closed-set outer Sexp algebra rather than as two inline algebra-constant enumerations at every consumer that iterates the paired delimiter axis. THEORY.md §V.1 — knowable platform; the paired-delimiter sub-vocabulary now binds as load-bearing typed data at the algebra level rather than as two per-site disjuncts. THEORY.md §VI.1 — generation over composition; the paired-delimiter (opener + closer) composition regenerates identically through this ONE typed forced-arity array rather than through two inline algebra-constant enumerations per consumer.

Source

pub const COMMENT_LEAD: char = ';'

Canonical ; char that begins a line-comment run in the reader’s tokenizer AND (as a bare-atom terminator disjunct) breaks a Token::Atom accumulator when the byte is encountered mid-lexeme. Outer-structural peer of Self::LIST_OPEN / Self::LIST_CLOSE on the reader-discard axis: where LIST_OPEN / LIST_CLOSE are the paired-delimiter constants that shape a Sexp::List payload on the closed-set outer Sexp algebra, COMMENT_LEAD is the ONE ; byte the reader’s tokenizer’s TWO comment-boundary sites bind to on the same outer algebra — the outer-dispatch arm that begins a line-comment run (consuming through the trailing \n which is itself absorbed by the whitespace disjunct in the outer match) AND the bare-atom terminator disjunct that ends a Token::Atom accumulator when it encounters this byte mid-lexeme so a bare foo;bar source tokenizes as Token::Atom("foo") @ 0 followed by a discarded line-comment run rather than as ONE Token::Atom("foo;bar") payload.

Pre-lift the same ';' byte lived inline at TWO sites in crate::reader::tokenize: the outer-match ';' => { … } line-comment arm AND the bare-atom terminator’s || ch == ';' disjunct. Post-lift the (reader-discard role, canonical byte) pairing binds at ONE constant on the Sexp algebra that both consumer sites route through; a refactor that swaps the byte (e.g. a Scheme R7RS-style port to #; datum-comment syntax, an Emacs-style port to #! shebang-comment syntax) touches ONE constant rather than two inline bytes that would silently drift out of tokenizer agreement if one was updated without the other.

Reader-discard contract: Sexp::COMMENT_LEAD MUST NOT surface as an atomic payload in any parsed Sexp — the outer-dispatch arm consumes the byte AND every char up to (but not past) the trailing \n, emitting NO token. The bare-atom terminator disjunct breaks the Token::Atom accumulator EXACTLY on this byte so the subsequent line-comment run reaches the outer arm with its byte-offset intact. Both sites bind to ONE constant so a regression that drifts ONE of the two disjuncts (e.g. re-inlines ';' at the outer arm while migrating the terminator to a different byte) fails at rustc / test time rather than as a silent tokenizer misclassification.

Cross-axis disjointness with the sibling markers (pinned structurally at sexp_comment_lead_distinct_from_every_other_algebra_marker): COMMENT_LEAD‘s byte MUST differ from Self::LIST_OPEN ('('), Self::LIST_CLOSE (')'), Atom::STR_DELIMITER ('"'), Atom::KEYWORD_MARKER’s lead byte (':'), the two Atom::bool_literal spellings’ lead byte ('#') AND every QuoteForm::lead_char projection ('\'', '`', ',') on the substrate’s outer-marker axes. Otherwise a bare ;foo lexeme would ambiguously route through the line-comment arm AND a sibling algebra’s arm at the reader’s outer dispatch.

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (reader-discard role, canonical ; byte) pairing binds at ONE constant on the closed-set outer Sexp algebra regardless of which of the two consumer sites reaches in. THEORY.md §VI.1 — generation over composition; two byte-identical inline ';' char literals across ONE substrate file collapse onto ONE named constant, matching the substrate’s three-times rule (\geq 2 PRIME-DIRECTIVE trigger). THEORY.md §V.1 — knowable platform; the canonical comment-lead byte becomes a TYPE-level constant on the outer substrate algebra rather than two inline bytes at two consumer surfaces in crate::reader.

Source

pub const COMMENT_TERM: char = '\n'

Canonical \n char that terminates a line-comment run in the reader’s tokenizer — the section-for-retraction sibling of Self::COMMENT_LEAD on the reader-discard axis. Paired opener/terminator peer of Self::LIST_OPEN / Self::LIST_CLOSE on the outer-structural axis: where Self::LIST_OPEN / Self::LIST_CLOSE are the two typed constants that shape a Sexp::List payload, Self::COMMENT_LEAD / Self::COMMENT_TERM are the two typed constants that shape the reader’s line-comment discard run. Both pairs live on the closed-set outer Sexp algebra so the reader-discard axis carries the same opener/closer discipline the outer-structural axis has carried since the initial Self::LIST_OPEN / Self::LIST_CLOSE lift.

Pre-lift the same '\n' byte lived inline at ONE site in crate::reader::tokenize — the line-comment discard loop’s terminator check if ch == '\n' { break; }. Post-lift the (reader-discard terminator role, canonical byte) pairing binds at ONE constant on the Sexp algebra that the consumer site routes through; a refactor that ports the reader to a different line-break convention (e.g. Scheme R7RS #; datum-comment terminated at the next well-formed datum, an Emacs-style port with \r\n CRLF sequences, a Common-Lisp-style #|…|# block comment closed by |#) touches ONE constant (or extends the algebra by ONE peer method) rather than an inline byte.

Reader-discard contract: Sexp::COMMENT_TERM MUST NOT surface as an atomic payload in any parsed Sexp — the reader’s Self::COMMENT_LEAD outer-dispatch arm consumes every byte (INCLUDING this terminator) up to and including the FIRST occurrence of Self::COMMENT_TERM, emitting NO token. The (lead, term) pair carries the SAME reader-discard invariant as (LIST_OPEN, LIST_CLOSE) does on the outer-structural axis: both bytes are structural markers that never appear in a token payload.

Cross-axis disjointness with the sibling closed-set markers (pinned structurally at sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker): COMMENT_TERM‘s byte MUST differ from every NON-whitespace outer-marker char — Self::LIST_OPEN, Self::LIST_CLOSE, Self::COMMENT_LEAD, Atom::STR_DELIMITER, Atom::STR_ESCAPE_LEAD, Atom::KEYWORD_MARKER’s lead byte, the two Atom::bool_literal spellings’ lead byte, AND every QuoteForm::lead_char projection. The terminator IS a whitespace char (it satisfies char::is_whitespace) so the disjointness test explicitly excludes the whitespace-family axis: the terminator’s role IS to be whitespace-family, so the disjointness contract binds only against the non-whitespace outer-marker axes.

Interaction with the escape-decode codomain axis: the terminator byte '\n' COINCIDES with the C0 control byte Atom::decode_str_escape('n') produces — the two roles are distinct algebraic axes (reader-discard structural marker on the outer Sexp algebra vs. Str-escape shorthand codomain value on the inner Atom algebra) so the collision at the byte level is by design, NOT a disjointness violation. A \n byte APPEARING inside a Token::Str payload’s decoded output (via \n shorthand) is orthogonal to a \n byte APPEARING as the line-comment terminator at the reader’s outer-dispatch discard loop.

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (reader-discard terminator role, canonical \n byte) pairing binds at ONE constant on the closed-set outer Sexp algebra regardless of which consumer reaches in. THEORY.md §II.1 invariant 5 — composition preserves proofs; the paired (COMMENT_LEAD, COMMENT_TERM) shape now lives at the algebra alongside (LIST_OPEN, LIST_CLOSE), so the reader-discard axis carries the SAME opener/closer discipline as the outer- structural axis. THEORY.md §VI.1 — generation over composition; the reader’s inline '\n' char literal at the line-comment discard loop’s terminator check collapses onto ONE named constant on the substrate algebra. THEORY.md §V.1 — knowable platform; the canonical line-comment terminator byte becomes a TYPE-level constant on the outer substrate algebra rather than an inline '\n' at one consumer surface in crate::reader::tokenize.

Source

pub const COMMENT_DELIMITERS: [char; 2]

Canonical paired line-comment delimiter closed-set ALL array — composes Self::COMMENT_LEAD followed by Self::COMMENT_TERM in canonical (opener, terminator) declaration order, forced-arity [char; 2] so a hypothetical alternative comment convention (a Scheme R7RS #; datum-comment lead paired with a next-datum terminator, an Emacs-style CRLF paired terminator, a Common-Lisp #|…|# block comment closed by |#) would extend this array + one algebra constant per new row in lockstep — rustc’s forced- arity check on [char; N] binds the extension through the array declaration site.

Cross-axis peer to Self::LIST_DELIMITERS ([char; 2] on the outer-structural paired-delimiter axis of the SAME closed-set outer Sexp algebra) — both close a paired-role byte sub-vocabulary at the SAME shape ([char; 2], (opener, closer_or_terminator) canonical declaration order) but on DISTINCT roles: Self::LIST_DELIMITERS closes the two typed constants that shape a Self::List payload (Self::LIST_OPEN / Self::LIST_CLOSE, BOTH of which classify as bare-atom boundaries and both non-whitespace); Self::COMMENT_DELIMITERS closes the two typed constants that shape the reader’s line-comment discard run (Self::COMMENT_LEAD / Self::COMMENT_TERM, where the LEAD row is a bare-atom boundary AND non-whitespace, and the TERM row is a whitespace-family char absorbed by the reader’s ch.is_whitespace() outer-dispatch arm rather than a distinct bare-atom boundary). The two arrays partition the SIX-category outer-dispatch arm-set into a per-axis paired shape (2 rows for list delimiters + 2 rows for comment delimiters + 1 row for Atom::STR_DELIMITER + 3-of-4 rows for QuoteForm::LEADS + the residual whitespace family), each axis carrying its own forced-arity ALL array.

Also sibling-shape to Atom::SELF_ESCAPE_TABLE ([char; 2] on the inner-Str-payload self-escape sub-vocabulary axis of the closed-set Atom algebra), Atom::BOOL_LITERALS ([&'static str; 2] on the Scheme-bool spelling axis), and crate::error::UnquoteForm::MARKERS / crate::error::UnquoteForm::IAC_FORGE_TAGS ([&'static str; 2] on the template-substitution subset algebra — the 2-of-4 subset carving of QuoteForm) — every closed-set outer projection on the substrate that carries a paired-role two-row axis now pins its canonical bytes at ONE pub const per role plus a forced-arity ALL array for family-wide consumers.

Pre-lift the two-element [Self::COMMENT_LEAD, Self::COMMENT_TERM] composition had NO typed source of truth on the substrate — the two constants each existed independently on the algebra (Self::COMMENT_LEAD shipped in the initial reader-discard lift; Self::COMMENT_TERM shipped in the follow-on paired- terminator lift bb1bd5e) and consumers that wanted the (opener, terminator) shape had to reach across the algebra through TWO pub const sites. Post-lift the paired-role sub-vocabulary binds at ONE forced-arity ALL array on the closed- set outer Sexp algebra alongside the peer Self::LIST_DELIMITERS array on the outer-structural axis; a consumer that walks EITHER axis of the outer-structural / reader-discard cross-product reads the paired-role identity off the shared [char; 2] shape.

Structural invariant carried at the SHAPE level: [char; 2] pairs section-for-retraction one-to-one with Self::LIST_DELIMITERS’s [char; 2] — the two arrays sit at distinct roles on the SAME closed-set outer Sexp algebra (outer-structural payload-delimiter role for LIST_DELIMITERS; reader-discard opener/terminator role for COMMENT_DELIMITERS) but share the same forced-arity shape at their respective axes. A consumer that reaches for one of the two arrays encodes its axis’s paired-role identity in the SHAPE it iterates rather than in a per-site convention.

Composition law (round-trip): COMMENT_DELIMITERS[0] == Self::COMMENT_LEAD AND COMMENT_DELIMITERS[1] == Self::COMMENT_TERM AND COMMENT_DELIMITERS.len() == 2. The forced-arity + canonical declaration order together pin every downstream index-sweep consumer to the (opener, terminator) pairing at rustc time; a reorder without reordering the underlying algebra constants fails at the composition pin below.

Path-uniformity contract pinned per-row: COMMENT_DELIMITERS[0] (the LEAD row) MUST classify as a bare-atom boundary via Self::is_bare_atom_boundary (the reader’s outer-dispatch’s dedicated line-comment arm is one of the SIX categories that projection covers), AND COMMENT_DELIMITERS[1] (the TERM row) MUST classify as a whitespace-family char via char::is_whitespace (the reader’s ch.is_whitespace() arm absorbs the terminator so the discard loop’s post-loop hand-off to the outer-dispatch fires the whitespace arm rather than a distinct comment-terminator arm). The per-row asymmetry is LOAD-BEARING and structurally distinct from Self::LIST_DELIMITERS’s BOTH-rows-are-bare-atom-boundaries contract (both ( and ) are non-whitespace outer-dispatch arms). Pinned by sexp_comment_delimiters_lead_row_is_bare_atom_boundary + sexp_comment_delimiters_term_row_is_whitespace_family_char.

Cross-axis disjointness pinned structurally at sexp_comment_delimiters_disjoint_from_list_delimiters: no row of COMMENT_DELIMITERS aliases any row of Self::LIST_DELIMITERS — the reader-discard sub-vocabulary and the outer-structural list-delimiter sub-vocabulary partition their respective bytes disjointly on the SAME closed-set outer Sexp algebra. Cross-algebra disjointness pinned at sexp_comment_delimiters_disjoint_from_str_delimiter: no row aliases Atom::STR_DELIMITER — the reader-discard arm and the Str-payload arm partition their bytes across the two closed-set algebras disjointly.

Future consumers that compose against Self::COMMENT_DELIMITERS: a hypothetical tatara_lisp_comment_delimiter_total{delimiter=";"|"\n"} Sekiban metric surface at Prometheus recording time — the label-set generator sweeps this array verbatim rather than re-typing the two paired bytes inline at each recorder, and rustc-binds the metric-label set to the closed set through the forced-arity ALL array; an LSP / structural-editor that highlights line-comment runs — the (opener, terminator) pair the editor spans over IS this array’s two rows; a hypothetical Sexp::BLOCK_COMMENT_DELIMITERS peer array for a future #|…|# block-comment mode would follow the same shape mechanically, extending the reader-discard axis by ONE peer array without touching this one’s shape.

Theory anchor: THEORY.md §III — the typescape; the paired (opener, terminator) reader-discard sub-vocabulary of the reader’s outer-dispatch arm-set now binds at ONE typed [char; 2] array on the closed-set outer Sexp algebra rather than as two independent algebra constants (Self::COMMENT_LEAD, Self::COMMENT_TERM) accessed independently at every consumer that wants the paired-role shape. The shared [char; 2] shape with Self::LIST_DELIMITERS encodes the paired-role identity relation across the two axes of the SAME closed-set algebra at the type system level. THEORY.md §V.1 — knowable platform; the paired-discard-delimiter sub-vocabulary becomes load-bearing typed data on the closed-set outer Sexp algebra. THEORY.md §VI.1 — generation over composition; the paired-delimiter (opener + terminator) composition regenerates identically through this ONE typed forced-arity array rather than through two independent algebra constants at every consumer. THEORY.md §II.1 invariant 5 — composition preserves proofs; the two-axis (outer-structural, reader-discard) cross-product on the closed- set outer Sexp algebra now carries the SAME opener/closer discipline on BOTH axes through two forced-arity ALL arrays with byte-identical shape.

Source

pub const NON_WHITESPACE_BARE_ATOM_TERMINATORS: [char; 7]

Closed-set forced-arity ALL array over the SEVEN non-whitespace category-leading chars the reader’s outer-dispatch cascade specialises on — the reader-level boundary sub-vocabulary that paired with char::is_whitespace() closes the six-clause Self::is_bare_atom_boundary disjunction. Composes through seven typed pub const primitives spanning THREE type namespaces on the SAME reader-outer-dispatch axis of the substrate:

Cross-axis peer to Self::LIST_DELIMITERS ([char; 2] on the outer-structural payload-delimiter axis) and Self::COMMENT_DELIMITERS ([char; 2] on the reader-discard opener/terminator axis) at ONE algebra level up: those two arrays close their respective paired-role sub-vocabularies (opener + closer, opener + terminator) on the reader-INNER axis of the outer Sexp algebra; this array closes the reader-OUTER-dispatch category-leading char sub-vocabulary across the SAME closed-set outer Sexp algebra PLUS its two sibling sub-algebras (QuoteForm, Atom) at ONE family-wide [char; 7] primitive. Sibling-shape peer of the intra-algebra sub-vocabulary array QuoteForm::LEADS ([char; 3] — the three DISTINCT quote-family reader-lead bytes) which this array embeds as its middle three positions: where QuoteForm::LEADS closes the quote-family sub-carving’s reader-lead sub-vocabulary at ONE forced-arity array on ONE closed-set algebra, this array closes the FULL reader-outer- dispatch non-whitespace category-leading char sub-vocabulary at ONE forced-arity array on the outer Sexp algebra by composing through the three-arm quote-family sub-carving + the two-arm structural-delimiter sub-carving + the two-arm atomic- carve delimiter + comment-lead singletons.

Pre-lift the seven category-leading chars had NO family-wide array on the outer Sexp algebra — Self::is_bare_atom_boundary carried them as three sub-expressions (Self::LIST_DELIMITERS.contains(&ch) on the structural-delimiter axis, QuoteForm::from_lead_char(ch).is_some() on the quote-family axis, ch == Atom::STR_DELIMITER || ch == Self::COMMENT_LEAD on the singleton axes) unified through boolean disjunction. Post-lift the WHOLE non-whitespace terminator sub- vocabulary binds at ONE pub const [char; 7] array on the outer Sexp algebra so Self::is_bare_atom_boundary collapses to ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch) — TWO clauses (one whitespace-partial predicate + one contains-check on the family-wide ARRAY) rather than five sub-clauses spanning three type namespaces. Consumers keyed on the whole family (a tatara-check predicate (check-reader- outer-dispatch-terminator-partition-injective …) that verifies the seven-arm partition structurally, a future REPL / LSP tokenizer-boundary hint that scans the source for the reader- outer-dispatch category-leading chars upfront, a future completion generator that suggests the seven bytes at every bare-atom-lexeme insertion site) read through Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS without re-deriving the three-part sub-expression composition inline.

Composition law (forward): for every ch: char, Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch) == (Self::LIST_DELIMITERS.contains(&ch) || QuoteForm::from_lead_char(ch).is_some() || ch == Atom::STR_DELIMITER || ch == Self::COMMENT_LEAD) — pinned by sexp_non_whitespace_bare_atom_terminators_agree_with_pre_lift_sub_expression_disjunction. Boundary-predicate composition law: Self::is_bare_atom_boundary(ch) == (ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch)) — pinned by sexp_is_bare_atom_boundary_agrees_with_terminators_array_on_non_whitespace_partition.

Adding a hypothetical seventh reader-outer-dispatch category (e.g. #|…|# block-comment lead byte, #\ char-literal prefix, #[ vector-literal prefix — each pinning a new lead byte on Self or a fresh sub-algebra) extends this array AND Self::is_bare_atom_boundary’s indirect coverage in LOCKSTEP — rustc’s forced-arity check on [char; 7] fails compilation if the algebra grows without the array (or the array without the algebra).

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (reader-outer-dispatch category, canonical char) family-wide pairing binds at ONE typed [char; 7] array on the outer Sexp algebra regardless of which of the three sub-algebras the individual chars name their per-role pub const primitive on. THEORY.md §III — the typescape; the seven canonical reader-outer-dispatch category-leading bytes bind at ONE typed [char; 7] array on the outer Sexp algebra rather than at three-part sub-expression composition inline at Self::is_bare_atom_boundary. THEORY.md §V.1 — knowable platform; the family’s cardinality becomes a TYPE-level constant on the substrate algebra rather than a per-consumer hand-rolled enumeration of the seven chars. THEORY.md §VI.1 — generation over composition; the family-wide contract sweeps (routing through typed sub-algebra pub const primitives, pairwise distinctness, agreement with the pre-lift sub-expression disjunction) emerge from the composition of EIGHT substrate primitives (this pub const [char; 7] array + the seven sub-algebra pub const primitives) rather than as inline disjunctions at each call site.

Source

pub fn is_bare_atom_boundary(ch: char) -> bool

Reader-level boundary predicate — returns true iff ch is one of the SIX outer-dispatch category-leading chars the reader’s tokenizer specialises on: whitespace, Self::LIST_OPEN, Self::LIST_CLOSE, any QuoteForm::lead_char (via the closed-set QuoteForm::from_lead_char decode), Atom::STR_DELIMITER, AND Self::COMMENT_LEAD. The ONE typed projection on the outer Sexp algebra that names the disjunction of “the char would start a NEW reader-level token (or a discarded run) rather than feed the current bare-atom accumulator.”

Structural dual of the reader’s outer-dispatch cascade in crate::reader::tokenize: the outer-dispatch has FIVE specific arms (ws if ws.is_whitespace(), Self::COMMENT_LEAD, Self::LIST_OPEN, Self::LIST_CLOSE, Atom::STR_DELIMITER) plus ONE pre-match QuoteForm::from_lead_char(c).is_some() gate — SIX categories in total. The default _ => { … bare-atom accumulator … } arm fires EXACTLY when every specific arm rejects. This method is the typed projection of that implicit disjunction: Sexp::is_bare_atom_boundary(ch) == true iff ch would trigger one of the SIX specific arms, and false iff ch would fall through to the bare-atom accumulator’s default arm. The two consumer sites in crate::reader::tokenize — the outer-dispatch’s implicit “no specific arm fires” residual predicate AND the bare-atom accumulator’s terminator disjunct — now share ONE typed source of truth on the closed-set outer Sexp algebra.

Pre-lift the SIX-clause boolean chain (ch.is_whitespace() || ch == Sexp::LIST_OPEN || ch == Sexp::LIST_CLOSE || QuoteForm::from_lead_char(ch).is_some() || ch == Atom::STR_DELIMITER || ch == Sexp::COMMENT_LEAD) lived inline at the bare-atom accumulator’s terminator gate in crate::reader::tokenize, spanning THREE type namespaces (Sexp, Atom, QuoteForm) at ONE consumer site. Post-lift the WHOLE disjunction binds at ONE typed projection on the outer Sexp algebra so a refactor that adds a SEVENTH outer-dispatch category (e.g. #|…|# block-comment lead byte, #\ char-literal prefix, #[ vector-literal prefix) extends the algebra ONCE (via a new arm on THIS method AND a matching outer-dispatch arm in the reader) rather than mutating an inline six-clause boolean chain that would silently drift out of tokenizer agreement if one clause was added without the other. Sibling-shape peer of the outer-dispatch’s closed-set per-category projections (QuoteForm::from_lead_char on the quote-family axis; Atom::decode_str_escape on the Str-escape axis): where those two methods each lift ONE outer-dispatch category’s decode onto its typed algebra, THIS method lifts the DISJUNCTION of ALL SIX outer-dispatch categories onto the outer Sexp algebra as a bool predicate.

Composition law (forward): for every char ch and every substrate-marker enumeration Self::{LIST_OPEN, LIST_CLOSE, COMMENT_LEAD}, Atom::STR_DELIMITER, QuoteForm::from_lead_char(ch).is_some(), is_bare_atom_boundary returns true; for every char that isn’t whitespace AND isn’t listed on any marker axis, returns false. Reader-level composition law: read(format!("foo{ch}")) tokenizes as [Token::Atom("foo"), …trailing token(s) from ch] when Self::is_bare_atom_boundary(ch) is true, and as [Token::Atom(format!("foo{ch}"))] (ONE token) when it is false.

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (reader-level boundary role, canonical char) pairing binds at ONE typed projection on the outer Sexp algebra regardless of which of the SIX outer-dispatch category-leading chars is under test. THEORY.md §VI.1 — generation over composition; a SIX-clause inline boolean disjunction spanning THREE type namespaces collapses onto ONE named method — the substrate’s three-times rule saturated at the outer-dispatch’s disjunction. THEORY.md §V.1 — knowable platform; the canonical reader-level boundary predicate becomes a TYPE-level method on the outer substrate algebra rather than an inline six-clause boolean chain at ONE consumer site inside crate::reader::tokenize.

Source

pub fn symbol(s: impl Into<String>) -> Sexp

Canonical Self::Atom-Atom::Symbol outer constructor — composes Atom::symbol (the typed-construct method on the closed-set Atom algebra) under the Self::Atom outer wrapper. The first of six Self::Atom(Atom::X(_)) outer constructors all routing through the typed Atom construct family at the inner algebra so the .into() coercion + tuple- variant constructor pair lives at ONE site per kind on the Atom algebra rather than at this outer constructor’s body. Sibling-shape lift to the [Atom::as_X] / [Self::as_X] composition through Self::as_atom on the projection axis: where projections route OUTER Self::as_X through self.as_atom().and_then(Atom::as_X), constructions route OUTER Self::X through Self::Atom(Atom::X(payload)).

Composition law (forward): Sexp::symbol(s) == Sexp::Atom(Atom::symbol(s)) for every s: impl Into<String>. Round-trip law (with the soft-projection sibling): for every s: &str, Sexp::symbol(s).as_symbol() == Some(s) — the inner algebra’s section-for-retraction surfaces through the outer algebra without re-derivation. Same posture across the six sibling pairs.

Source

pub fn keyword(s: impl Into<String>) -> Sexp

Canonical Self::Atom-Atom::Keyword outer constructor — composes Atom::keyword under Self::Atom. See Self::symbol for the outer-algebra docstring.

Source

pub fn string(s: impl Into<String>) -> Sexp

Canonical Self::Atom-Atom::Str outer constructor — composes Atom::string under Self::Atom.

Source

pub fn int(n: i64) -> Sexp

Canonical Self::Atom-Atom::Int outer constructor — composes Atom::int under Self::Atom.

Source

pub fn float(n: f64) -> Sexp

Canonical Self::Atom-Atom::Float outer constructor — composes Atom::float under Self::Atom.

Source

pub fn boolean(b: bool) -> Sexp

Canonical Self::Atom-Atom::Bool outer constructor — composes Atom::boolean under Self::Atom.

Source

pub fn quote(inner: Sexp) -> Sexp

Canonical Self::Quote outer constructor — composes QuoteForm::wrap on the QuoteForm::Quote marker so the Box::new(inner) allocation + tuple-variant pair lives at ONE site on the closed-set QuoteForm algebra rather than at this outer-constructor body. The first of four Self::Quote* outer constructors all routing through the typed QuoteForm::wrap family at the inner algebra — the quote-family-axis section peer of the six Self::Atom(Atom::X(_)) outer constructors (Self::symbol, Self::keyword, Self::string, Self::int, Self::float, Self::boolean) all routing through the typed Atom construct family on the atomic-payload axis. Sibling-shape lift to the Self::as_quote_form soft-projection sibling on the projection axis: where the projection soft-decomposes a quote-family wrapper into Option<(QuoteForm, &Sexp)> (surfacing the typed marker alongside the borrowed inner body), each of these four typed constructors embeds a fresh inner body under the typed marker into the matching tuple-variant wrapper.

Composition law (forward): Sexp::quote(inner) == QuoteForm::Quote.wrap(inner) == Sexp::Quote(Box::new(inner)) for every inner: Sexp. Round-trip law (section-for-retraction with the soft-projection sibling): Sexp::quote(inner) .as_quote_form() == Some((QuoteForm::Quote, &inner)) for every inner: Sexp — the inner algebra’s typed constructor pairs section-for-retraction with the outer algebra’s soft projection, and the marker + inner body cross-projection preserves identity. Same posture across the four sibling pairs (Sexp::quote / Sexp::quasiquote / Sexp::unquote / Sexp::unquote_splice).

Pre-lift the Self::Quote(Box::new(inner)) welded triple (Self::Quote, Box::new, inner) appeared inline at every consumer that builds a quote-family wrapper — well past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is named. The welded triple already lives at ONE site on the closed-set QuoteForm::wrap algebra for the marker-driven consumer path; this outer constructor binds the per-variant Sexp::X(Box::new( inner)) welded triple to ONE typed-algebra method per marker on the outer Sexp algebra, so consumers that know the marker at compile time bind to the typed method directly rather than re-deriving the Self::X(Box::new(_)) pair inline. A future allocation-policy change (e.g. arena-allocated wrappers for span-aware Sexp) lands as ONE edit at QuoteForm::wrap (the single site the allocation composition lives) and propagates through these four typed constructors byte-for-byte.

Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the (QuoteForm variant, Sexp tuple-variant constructor) pairing binds at ONE typed-algebra method per marker on the outer Sexp algebra regardless of which consumer reaches in. THEORY.md §VI.1 — generation over composition; the welded Self::X(Box::new(_)) triple at every quote-family construct site regenerates through QuoteForm::X.wrap(_) composition over the typed algebra rather than per-site re-derivation. THEORY.md §V.1 — knowable platform; the typed-construct family becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed-project family Self::as_quote_form rather than as bare tuple-variant constructor + per-site Box::new discipline. A future fifth homoiconic prefix syntax (e.g. syntax quotation #'x for hygienic macros) extends QuoteForm::ALL + QuoteForm::wrap’s arm + this construct family in lockstep, rustc-enforced through the closed-set exhaustiveness.

Frontier inspiration: Racket’s (quote x) / (quasiquote x) / (unquote x) / (unquote-splicing x) typed syntactic-form construct face paired one-for-one with the Self::as_quote_form closed-set soft-projection sibling on the outer syntax algebra — the typed-construct + typed-project algebra dual is closed at one method per direction per marker on Racket’s surface, and the Self::quote / Self::quasiquote / Self::unquote / Self::unquote_splice family is the Rust-typed peer on the closed-set outer Sexp algebra with QuoteForm::wrap standing in for Racket’s typed dispatch face. MLIR’s mlir::OpBuilder::create<QuoteOp>(loc, inner) typed-IR wrapper construction paired with mlir::dyn_cast<QuoteOp>(op) on the projection face — the typed factory + typed downcast pair the IR algebra closes over on every wrapper op; Self::quote / Self::as_quote_form is the Rust-typed peer on the outer Sexp algebra with the closed-set QuoteForm standing in for MLIR’s OperationName taxonomy over the wrapper-op family.

Source

pub fn quasiquote(inner: Sexp) -> Sexp

Canonical Self::Quasiquote outer constructor — composes QuoteForm::wrap on the QuoteForm::Quasiquote marker. See Self::quote for the outer-algebra docstring.

Source

pub fn unquote(inner: Sexp) -> Sexp

Canonical Self::Unquote outer constructor — composes QuoteForm::wrap on the QuoteForm::Unquote marker. See Self::quote for the outer-algebra docstring.

Source

pub fn unquote_splice(inner: Sexp) -> Sexp

Canonical Self::UnquoteSplice outer constructor — composes QuoteForm::wrap on the QuoteForm::UnquoteSplice marker. See Self::quote for the outer-algebra docstring.

Source

pub fn quote_form(marker: QuoteForm, inner: Sexp) -> Sexp

Canonical marker-driven quote-family outer constructor — routes through QuoteForm::wrap on the caller-supplied QuoteForm marker at ONE site on the closed-set Sexp algebra. The outer- algebra section-for-retraction sibling of the existing Self::as_quote_form soft-projection (Option<(QuoteForm, &Sexp)>): where the projection soft-decomposes a quote-family wrapper into its typed QuoteForm marker + borrowed inner body on the (marker, borrowed-inner) product, this constructor embeds a typed QuoteForm marker + owned inner body pair into the matching tuple-variant wrapper on the (marker, owned-inner) product. Marker-driven parent of the four per-variant siblings Self::quote / Self::quasiquote / Self::unquote / Self::unquote_splice — each of the four is Self::quote_form( QuoteForm::X, inner) restricted to a compile-time-known marker; this constructor is the marker-abstracted parent every consumer that binds the marker as a runtime value routes through.

Sibling posture across the outer-algebra construct-family layer: where Self::call and Self::named_call close the (construct, project) dual on the call-form + named- call-form typed decompositions of the residual-axis List arm, and Self::list closes it on the residual-axis List arm itself, this constructor closes it on the quote-family- axis wrapper decomposition — the outer Sexp algebra now carries a (marker, project) construct-family dual pair for every axis of the SexpShape closed set at ONE typed method per corner, with Sexp::quote_form(qf, inner) as the marker-driven quote-family construct entry and Self::as_quote_form as its marker-recovering projection sibling.

Composition law (forward): Sexp::quote_form(marker, inner) == marker.wrap(inner) for every marker: QuoteForm and every inner: Sexp. The body routes through the SAME closed-set QuoteForm::wrap method the four per-variant siblings (Self::quote / Self::quasiquote / Self::unquote / Self::unquote_splice) already reach for, so the (marker, Sexp tuple-variant constructor) pairing binds at ONE closed- set match on the substrate algebra — a regression that drifts one consumer’s marker→wrapper mapping from the others (e.g. a copy-edit that pairs QuoteForm::Quote with the Sexp::Quasiquote tuple variant, or that drops a QuoteForm::UnquoteSplice value through the Sexp::Unquote tuple variant) cannot reach the substrate’s runtime.

Round-trip law (section-for-retraction with the outer-algebra soft-projection): for every marker: QuoteForm and every inner: Sexp, Sexp::quote_form(marker, inner.clone()) .as_quote_form() == Some((marker, &inner)) — the outer algebra’s marker-driven quote-family constructor pairs section- for-retraction with the outer algebra’s soft quote-family projection, and the (marker, inner body) cross-projection preserves identity for every QuoteForm variant.

Marker-recovering projection composition: Sexp::quote_form( marker, inner).as_quote_form_marker() == Some(marker) for every input — the marker-only projection sibling (Self::as_quote_form_marker) recovers the constructor’s marker byte-for-byte. Outer-shape composition law: Sexp::quote_form(marker, inner).shape() == marker.sexp_shape() — the outer-shape identity binds through the typed-shape lattice at ONE arm per QuoteForm variant, symmetric with the atomic construct family’s Sexp::X_atom(payload).shape() == AtomKind::X.sexp_shape() composition and the residual construct family’s Sexp::list(items).shape() == SexpShape::List composition.

Per-variant restriction laws (structural identity between the marker-driven parent + the four per-variant siblings):

  • Sexp::quote_form(QuoteForm::Quote, inner) == Sexp::quote(inner)
  • Sexp::quote_form(QuoteForm::Quasiquote, inner) == Sexp::quasiquote(inner)
  • Sexp::quote_form(QuoteForm::Unquote, inner) == Sexp::unquote(inner)
  • Sexp::quote_form(QuoteForm::UnquoteSplice, inner) == Sexp::unquote_splice(inner)

The four per-variant constructors ARE the marker-driven parent specialized on a compile-time-known marker; the marker-abstracted parent binds every consumer that routes a runtime QuoteForm value through a quote-family construct to ONE typed method on the outer Sexp algebra rather than a four-arm inline match qf { QuoteForm::X => Sexp::x(inner), … } dispatch.

Pre-lift consumers with a runtime QuoteForm marker routed through marker.wrap(inner) directly (the reader’s read_quoted production consumer at reader.rs, the domain module’s quote-family round-trip test site) — well past the ≥2 PRIME-DIRECTIVE trigger once the marker-driven pattern is named. Post-lift consumers bind to ONE typed-algebra method on the outer Sexp algebra sitting next to the typed-project family (Self::as_quote_form / Self::as_quote_form_marker) rather than reaching into the closed-set QuoteForm::wrap method directly. A future allocation-policy change (e.g. arena- allocated wrappers for span-aware Sexp) lands as ONE edit at the single QuoteForm::wrap composition site and propagates through this constructor byte-for-byte.

Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the (typed QuoteForm marker, owned inner body, QuoteForm::wrap composition) triple binds at ONE typed-algebra method on the outer Sexp algebra, closing the marker-driven quote-family (construct, project) algebra dual pair with Self::as_quote_form on the projection side. THEORY.md §II.1 invariant 2 — free middle; every consumer that has a runtime QuoteForm marker + an owned inner body and wants to build a quote-family wrapper routes through the SAME typed method, so a regression that drifts one consumer’s marker→wrapper mapping cannot reach the substrate’s runtime. THEORY.md §V.1 — knowable platform; the marker-driven quote-family typed-construct becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed-project family Self::as_quote_form / Self::as_quote_form_marker rather than the closed-set QuoteForm::wrap method threaded as a method call on a bound-marker value. THEORY.md §VI.1 — generation over composition; the marker-driven quote-family pair emerges from ONE typed-algebra composition through QuoteForm::wrap rather than from per-consumer marker→wrapper dispatch literals; a future fifth homoiconic prefix syntax (e.g. #'x for hygienic macros) extends QuoteForm::ALL + QuoteForm::wrap’s match arm + Self::as_quote_form’s match arm in lockstep — rustc-enforced through the closed-set exhaustiveness — with THIS constructor inheriting the extension through the QuoteForm::wrap composition site without a per- site edit.

Frontier inspiration: Racket’s (datum->syntax stx (list #'qf inner)) marker-abstracted quote-family construct paired one- for-one with syntax-e on the projection face — the typed- construct + typed-project algebra dual is closed on Racket’s syntax algebra at one method per direction, and Sexp::quote_form / Sexp::as_quote_form is the Rust-typed peer on the closed-set outer Sexp algebra with QuoteForm standing in for Racket’s syntactic-form taxonomy over the four homoiconic prefix wrappers. MLIR’s typed-IR mlir::OpBuilder::create(loc, OperationName, operands) marker- driven op construction paired with mlir::Operation::getName() on the projection face — the typed factory + typed downcast pair the IR algebra closes over on every op kind at one method per direction; Sexp::quote_form / Self::as_quote_form_marker is the Rust-typed peer on the outer Sexp algebra with the closed-set QuoteForm standing in for MLIR’s OperationName taxonomy over the four homoiconic prefix-wrapper op kinds.

Source

pub fn unquote_form(marker: UnquoteForm, inner: Sexp) -> Sexp

Canonical marker-driven template-substitution outer constructor — routes through UnquoteForm::wrap on the caller-supplied UnquoteForm marker at ONE site on the closed-set Sexp algebra. Subset-algebra peer of the marker-driven quote-family parent Self::quote_form: where Self::quote_form embeds a caller-supplied QuoteForm marker + owned inner body on the 4-of-12 quote-family carving through the closed-set QuoteForm::wrap composition site, THIS constructor embeds a caller-supplied UnquoteForm marker + owned inner body on the 2-of-12 template-substitution subset carving through the UnquoteForm::wrap composition site (which itself composes UnquoteForm::to_quote_form then QuoteForm::wrap, so the welded Sexp::X(Box::new(_)) triple ultimately still binds at the ONE canonical QuoteForm::wrap site the four per-variant siblings Self::quote / Self::quasiquote / Self::unquote / Self::unquote_splice and the marker-driven parent Self::quote_form all route through). Closes the (construct, project) algebra dual on the (UnquoteForm, Sexp) product against the pre-existing projection sibling Self::as_unquote (soft-decomposition into Option<(UnquoteForm, &Sexp)>) and its marker-only peer Self::as_unquote_form (soft-decomposition into Option<UnquoteForm>) — post-lift the outer Sexp algebra carries a marker-driven (construct, project) dual pair Sexp::unquote_form / Sexp::as_unquote at ONE typed method per direction on the template-substitution subset alongside the marker-only projection sibling Self::as_unquote_form, symmetric with the pair Sexp::quote_form / Sexp::as_quote_form / Sexp::as_quote_form_marker the superset carries.

Sibling posture across the outer-algebra construct-family layer: where Self::call and Self::named_call close the (construct, project) dual on the call-form + named-call- form typed decompositions of the residual-axis List arm, Self::list closes it on the residual-axis List arm itself, and Self::quote_form closes it on the quote-family-axis marker-driven decomposition (the parent 4-of-12 quote-family carving), THIS constructor closes it on the template-substitution-subset marker-driven decomposition (the 2-of-4 subset of the quote-family carving, equivalently the 2-of-12 substitution carving of the outer SexpShape closed set) — the outer Sexp algebra now carries a marker-driven construct-family dual pair for every closed-set carving on the quote-family axis at ONE typed method per corner.

Composition law (forward): Sexp::unquote_form(marker, inner) == marker.wrap(inner) for every marker: UnquoteForm and every inner: Sexp. The body routes through the SAME UnquoteForm::wrap method the subset-algebra consumer path already reaches for, so the (subset marker, Sexp tuple-variant wrapper) pairing binds at ONE closed-set composition site on the substrate — a regression that drifts one consumer’s subset marker → wrapper mapping from the others (e.g. a copy-edit that pairs UnquoteForm::Unquote with the Sexp::UnquoteSplice tuple variant, or that drops a UnquoteForm::Splice value through the Sexp::Unquote tuple variant) cannot reach the substrate’s runtime.

Round-trip law (section-for-retraction with the outer-algebra soft-projection): for every marker: UnquoteForm and every inner: Sexp, Sexp::unquote_form(marker, inner.clone()) .as_unquote() == Some((marker, &inner)) — the outer algebra’s marker-driven template-substitution constructor pairs section- for-retraction with the outer algebra’s soft template-substitution projection, and the (subset marker, inner body) cross-projection preserves identity for every UnquoteForm variant.

Marker-recovering projection composition: Sexp::unquote_form( marker, inner).as_unquote_form() == Some(marker) for every input — the marker-only projection sibling Self::as_unquote_form recovers the constructor’s subset marker byte-for-byte. Outer- shape composition law: Sexp::unquote_form(marker, inner).shape() == marker.sexp_shape() — the outer-shape identity binds through the typed-shape lattice at ONE arm per UnquoteForm variant, symmetric with the quote-family construct family’s Sexp::quote_form(marker, inner).shape() == marker.sexp_shape() composition and the atomic construct family’s Sexp::X_atom(payload).shape() == AtomKind::X.sexp_shape() composition. Superset-routing composition law: Sexp::unquote_form(marker, inner) == Sexp::quote_form( marker.to_quote_form(), inner) for every input — the subset- algebra construct routes through the same closed-set QuoteForm::wrap composition site the superset construct routes through, threaded via the typed 2-of-4 subset → superset projection UnquoteForm::to_quote_form. A regression that drifts either direction of this composition fails at the superset-routing pin.

Per-variant restriction laws (structural identity between the marker-driven parent + the two per-variant siblings):

  • Sexp::unquote_form(UnquoteForm::Unquote, inner) == Sexp::unquote(inner)
  • Sexp::unquote_form(UnquoteForm::Splice, inner) == Sexp::unquote_splice(inner)

The two per-variant constructors ARE the marker-driven parent specialized on a compile-time-known subset marker; the marker- abstracted parent binds every consumer that routes a runtime UnquoteForm value through a template-substitution construct to ONE typed method on the outer Sexp algebra rather than a two-arm inline match uf { UnquoteForm::Unquote => Sexp::unquote( inner), UnquoteForm::Splice => Sexp::unquote_splice(inner) } dispatch.

Pre-lift consumers with a runtime UnquoteForm marker routed through marker.wrap(inner) directly (reaching into the UnquoteForm::wrap subset-algebra method) OR through the two- step Sexp::quote_form(marker.to_quote_form(), inner) composition (routing via the superset marker-driven parent). Post-lift consumers bind to ONE typed-algebra method on the outer Sexp algebra sitting next to the typed-project family (Self::as_unquote / Self::as_unquote_form) rather than reaching into the closed-set UnquoteForm::wrap method directly or composing the superset parent with the subset → superset projection. A future allocation-policy change (e.g. arena-allocated wrappers for span-aware Sexp) lands as ONE edit at the single QuoteForm::wrap composition site and propagates through this constructor byte-for-byte (via the UnquoteForm::wrap composition).

Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the (typed UnquoteForm marker, owned inner body, UnquoteForm::wrap composition) triple binds at ONE typed- algebra method on the outer Sexp algebra, closing the marker- driven template-substitution (construct, project) algebra dual pair with Self::as_unquote on the projection side. THEORY.md §II.1 invariant 2 — free middle; every consumer that has a runtime UnquoteForm marker + an owned inner body and wants to build a template-substitution wrapper routes through the SAME typed method, so a regression that drifts one consumer’s subset marker → wrapper mapping cannot reach the substrate’s runtime. THEORY.md §V.1 — knowable platform; the marker-driven template- substitution typed-construct becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed- project family Self::as_unquote / Self::as_unquote_form rather than the closed-set UnquoteForm::wrap method threaded as a method call on a bound-marker value or the two-step subset → superset then Self::quote_form composition. THEORY.md §VI.1 — generation over composition; the marker-driven template- substitution pair emerges from ONE typed-algebra composition through UnquoteForm::wrap rather than from per-consumer subset-marker → wrapper dispatch literals; a future third template-substitution marker (e.g. a ,~ reverse-unquote) extends UnquoteForm::ALL + UnquoteForm::to_quote_form’s dispatch table in lockstep — rustc-enforced through the closed- set exhaustiveness — with THIS constructor inheriting the extension through the UnquoteForm::wrap composition site without a per-site edit.

Frontier inspiration: Racket’s (datum->syntax stx (list #'uf inner)) marker-abstracted template-substitution construct restricted to the substitution-subset of syntactic-form kinds, paired one-for-one with syntax-e on the projection face — the typed-construct + typed-project algebra dual is closed on Racket’s syntax algebra at one method per direction per subset, and Sexp::unquote_form / Sexp::as_unquote is the Rust-typed peer on the closed-set outer Sexp algebra with UnquoteForm standing in for Racket’s substitution-subset syntactic-form taxonomy. MLIR’s typed factory mlir::OpBuilder::create<UnquoteFamilyOp>(loc, marker, operands) paired with the projection sibling mlir::dyn_cast<UnquoteFamilyOp>(op) — the typed factory + typed downcast pair the IR algebra closes over on every op-family subset at one method per direction; Sexp::unquote_form / Self::as_unquote_form is the Rust-typed peer on the outer Sexp algebra with the closed-set UnquoteForm standing in for MLIR’s OperationName subset taxonomy over the template- substitution op family.

Source

pub fn is_list(&self) -> bool

Source

pub fn as_list(&self) -> Option<&[Sexp]>

Source

pub fn list<I>(items: I) -> Sexp
where I: IntoIterator<Item = Sexp>,

Canonical Self::List outer constructor — collects an impl IntoIterator<Item = Sexp> into the tuple-variant payload Vec<Sexp> at ONE site on the closed-set Sexp algebra. The residual-axis section-for-retraction sibling of the existing Self::as_list soft-projection ([Option<&[Sexp]>]): where the projection soft-decomposes a Self::List arm into its borrowed inner slice, this constructor embeds a fresh owned item sequence into the matching tuple-variant wrapper. Sibling of the atomic-payload construct family (Self::symbol, Self::keyword, Self::string, Self::int, Self::float, Self::boolean — all routing through the typed Atom construct family on the 6-of-12 atomic-payload carving) and the quote-family construct family (Self::quote, Self::quasiquote, Self::unquote, Self::unquote_splice — all routing through the typed QuoteForm::wrap family on the 4-of-12 quote-family carving); closes the (construct, project) algebra dual on the third and final structural carving of the outer Sexp closed set — the 2-of-12 residual axis covering Self::Nil and Self::List. Self::Nil is a unit variant carrying no payload — the residual-axis construct family closes at ONE constructor (this method) for the sole payload-bearing residual arm.

Composition law (forward): Sexp::list(items) == Sexp::List(items.into_iter().collect::<Vec<Sexp>>()) for every items: impl IntoIterator<Item = Sexp>. Round-trip law (section-for-retraction with the soft-projection sibling): for every items: Vec<Sexp>, Sexp::list(items.clone()).as_list() == Some(items.as_slice()) — the outer algebra’s typed constructor pairs section-for-retraction with the outer algebra’s soft projection, and the borrowed-slice cross- projection preserves identity. Sibling posture across the three axis-construct families on the outer Sexp algebra (atomic + quote-family + residual).

Outer-shape composition law: Sexp::list(items).shape() == SexpShape::List for every items: impl IntoIterator<Item = Sexp> — the residual-arm outer-shape identity binds through the typed-shape lattice at ONE arm, symmetric with the quote-family construct family’s outer-shape composition Sexp::X_variant(inner).shape() == QuoteForm::X.sexp_shape() and the atomic construct family’s Sexp::X_atom(payload).shape() == AtomKind::X.sexp_shape(). Structural-carving-marker composition law: Sexp::list(items).as_structural_kind() == Some(StructuralKind::List) for every items: impl IntoIterator<Item = Sexp> — the residual-axis carving marker binds through the closed-set StructuralKind algebra at ONE arm, symmetric with the atomic-axis’s Sexp::X_atom(payload) .as_atom_kind() == Some(AtomKind::X) marker composition.

Pre-lift the [Self::List(Vec<Sexp>)] welded pair (Self::List tuple-variant constructor + Vec<Sexp> payload) appeared inline at every consumer that builds a list-shaped Sexp value — well past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is named. Post-lift the welded pair binds at ONE typed-algebra method on the outer Sexp algebra with an impl IntoIterator<Item = Sexp> bound so consumers that have a Vec<Sexp>, a [Sexp; N] array, an iter().cloned() sequence, a .map(...).collect()-worthy chain, or a once(head).chain(tail) composition can hand the sequence directly to the algebra without a per-site .collect::<Vec< Sexp>>() coercion. A future allocation-policy change (e.g. arena-allocated lists for span-aware Sexp) lands as ONE edit at this method site and propagates through consumers byte-for-byte.

Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the (list-shaped inner sequence, Self::List tuple-variant constructor) pairing binds at ONE typed-algebra method on the outer Sexp algebra, closing the outer-algebra construct family across ALL THREE structural carvings of the SexpShape closed set (atomic-payload + quote-family + residual). THEORY.md §II.1 invariant 2 — free middle; every consumer that has an owned or iterable sequence of Sexp and wants to build a list-shaped wrapper routes through the SAME typed method, so a regression that drifts one consumer’s construction from the others cannot reach the substrate’s runtime. THEORY.md §V.1 — knowable platform; the typed-construct family becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed-project family Self::as_list rather than bare tuple-variant constructor + per-site Vec<Sexp> discipline. THEORY.md §VI.1 — generation over composition; the residual- arm outer-shape + carving-marker pairings emerge from ONE typed-algebra composition on the outer Sexp algebra rather than from per-consumer per-variant literals.

Frontier inspiration: Racket’s (list x y z) typed list- construct primitive paired one-for-one with (list? v) / (car v) / (cdr v) predicate/projection siblings on the same closed-set list shape — the typed-construct + typed- project algebra dual is closed at one method per direction on Racket’s surface, and Self::list / Self::as_list is the Rust-typed peer on the closed-set outer Sexp algebra with impl IntoIterator<Item = Sexp> standing in for Racket’s variadic collect face. MLIR’s mlir::OpBuilder::create< ListOp>(loc, elements) typed-IR list-op construction paired with mlir::dyn_cast<ListOp>(op) on the projection face — the typed factory + typed downcast pair the IR algebra closes over on every list-shaped op; Self::list / Self::as_list is the Rust-typed peer on the outer Sexp algebra with StructuralKind::List standing in for MLIR’s OperationName taxonomy over the list-shaped op family.

Source

pub fn as_structural_kind(&self) -> Option<StructuralKind>

Soft projection onto the closed-set StructuralKind residual carving marker — the 2-of-12 carving of the SexpShape algebra covering Self::Nil and Self::List (the outer shapes that lie OUTSIDE both the atomic-payload carving AtomKind and the quote-family carving QuoteForm). Returns Some(StructuralKind::Nil) for Self::Nil, Some(StructuralKind::List) for Self::List, None for every other outer shape (every Self::Atom variant, every quote-family wrapper: Self::Quote, Self::Quasiquote, Self::Unquote, Self::UnquoteSplice).

Sibling soft-projection peer of Self::as_quote_form (the soft-decomposition of the four homoiconic prefix wrappers into (QuoteForm, &Sexp)) and Self::as_unquote (the soft-decomposition of the two template-substitution wrappers into (UnquoteForm, &Sexp)). Direct value-level peer of the shape-level projection SexpShape::as_structural_kind — the pair (Sexp::as_structural_kind, SexpShape::as_structural_kind) binds the (Sexp value, StructuralKind carving marker) pairing at ONE typed method on each algebra, symmetric with the existing (Sexp value → AtomKind via Sexp::as_atom().map(Atom::kind)) atomic-axis composition and the direct (Sexp value → QuoteForm) marker projection Self::as_quote_form returns.

Composition law: s.as_structural_kind() == s.shape().as_structural_kind() for every s: &Sexp. Pre-lift the residual-carving marker at the value level was reachable only via the two-step composition s.shape().as_structural_kind() (walking through the full 12-variant SexpShape closed set to arrive at the 2-of-12 carving marker); post-lift the composition lands at ONE typed method on the value algebra — the Nil arm returns Some(Nil) directly and the List arm returns Some(List) directly, matching the residual-carving membership at the value level. The composition law is pinned by sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant in this module, so a regression that drifts either projection from the other surfaces immediately.

Sibling-shape lift to Self::is_list (the bare List-arm predicate) and Self::is_kwargs_list (the narrower kwargs-shaped List cohort predicate): where is_list returns true iff the value inhabits the List arm of the residual carving, as_structural_kind returns the typed carving marker that binds BOTH residual arms (Nil and List) at ONE typed projection — the operator answering “which residual arm?” rather than the bare “is this the List arm?” predicate.

Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp variant, StructuralKind carving marker) pairing becomes a TYPE projection on the substrate Sexp algebra rather than a two-step composition through the shape-level projection. A typo or swap at the value-projection site is no longer a runtime drift but a compile error against the typed projection. THEORY.md §VI.1 — generation over composition; the residual-carving marker projection now lives on the typed Sexp algebra alongside Self::as_atom, Self::as_list, Self::as_quote_form, Self::as_unquote, completing the (Sexp value → closed-set carving marker) family at the residual axis. THEORY.md §II.1 invariant 2 — free middle; every consumer that needs the residual-carving marker at the value level (a future tatara-check predicate keyed on the Nil/List cohort, a future LSP structural-navigation filter that keys on the residual carving, a future typed-rewriter walk over the residual arm) binds to ONE typed method on the value algebra rather than a two-step composition through the shape-level projection.

Frontier inspiration: MLIR’s mlir::dyn_cast<StructuralOp>(val) typed soft-downcast on the residual carving of a closed-set value algebra — the (value, typed carving marker) pairing lives at ONE typed projection on the outer value-algebra sibling. The Rust-typed peer here uses the substrate’s outer Sexp algebra with Sexp::as_structural_kind closing the residual-carving cell of the value-level soft-projection surface, symmetric with the atomic-axis composition through Self::as_atom and the quote-family projection Self::as_quote_form.

Source

pub fn is_kwargs_list(&self) -> bool

Structural-shape predicate — true iff this is a Self::List whose items form a non-empty, even-length (:k v :k v …) kwargs sequence with every even-indexed item being an Atom::Keyword. false for every other outer shape (Self::Nil, every Self::Atom variant, every quote-family wrapper) and for every Self::List that fails the kwargs convention (empty list, odd length, or any even-indexed non-keyword).

The structural witness that Self::to_json will project this value as serde_json::Value::Object rather than serde_json::Value::Array at the Self::List arm — the (Sexp variant + kwargs shape, JSON canonical-form) pairing binds at ONE inherent method on the algebra rather than at a free function consumers must reach into the domain module path to invoke. Inverse round-trip law: every Self::from_json projection of a serde_json::Value::Object satisfies this predicate (the Self::List arm Self::from_json builds for an Object is non-empty by the Object’s non-empty-keys invariant when present, even-length by the alternating :k v build, and keyword-headed at every even index by the Self::keyword(camel_to_kebab(k)) build — except for the structurally degenerate empty Object which projects to Sexp::List(vec![]) and returns false here, matching Self::to_json’s “empty-list ↛ kwargs” gate).

Composes through Self::as_list (the structural soft-projection onto &[Sexp]) and Atom::as_keyword (the typed soft-projection onto the keyword payload from the Atom algebra) — the predicate is rebuilt from already-lifted algebra primitives rather than inline-matching the Self::List arm. Sibling-shape predicate peer of Self::is_list (the unconditional Self::List-arm predicate), with this method narrowing the structural witness to the kwargs-shaped sub-cohort. The two predicates partition the list-typed cell of the algebra: every Self::List either satisfies is_kwargs_list (projects as serde_json::Value::Object through Self::to_json) or does not (projects as serde_json::Value::Array).

Theory anchor: THEORY.md §VI.1 — generation over composition; the kwargs-shape predicate, previously a pub(crate) free function in domain.rs reached across the module boundary by Self::to_json, is lifted ONE algebra level higher onto the inherent method on the Sexp algebra — completing the structural-predicate family alongside Self::is_list and the soft-projection family (Self::as_atom, Self::as_list, Self::as_quote_form). THEORY.md §II.1 invariant 2 — free middle; every consumer that queries “would Self::to_json project this as Object?” (the Self::to_json arm itself, future authoring-tool diagnostics, a future LSP completion fallback, a future REPL pretty-printer that chooses between (…) and {…} rendering, a future tatara-check typed-pattern matcher) routes through ONE inherent algebra method rather than reaching into the domain module path for a free function. THEORY.md §V.1 — knowable platform; the JSON-format witness becomes a TYPE projection on the substrate Sexp algebra next to its sibling Sexp::is_list / Sexp::as_list pair rather than living in a domain.rs pub(crate) helper consumers must import via module path.

Frontier inspiration: MLIR’s mlir::Operation::hasTrait<T>() — typed-IR operations carry their structural traits as inherent methods on the operation algebra rather than as free functions in a sibling module; Sexp::is_kwargs_list is the unstructured-Rust peer on the Sexp algebra for the “would-this-project-as-Object” structural trait. Racket’s (keyword-apply-procedure? stx) — the syntax-class predicate that gates a kwargs-style application form’s printer / expander path on the syntax algebra; Sexp::is_kwargs_list is the substrate’s peer at the Sexp layer, with the as_list(). is_some_and(…) composition standing in for Racket’s syntax-parse pattern matcher.

Source

pub fn as_atom(&self) -> Option<&Atom>

Soft projection onto the inner Atom payload — Some(&Atom) iff this is a Self::Atom variant, None for every other outer shape (Nil, List, Quote, Quasiquote, Unquote, UnquoteSplice). The structural-lift face of the per-atomic- payload soft-projection family — composes with the typed Atom::as_symbol / Atom::as_keyword / Atom::as_string / Atom::as_int / Atom::as_float / Atom::as_bool projections to give the six Sexp::as_X consumers ONE typed boundary instead of six inline Self::Atom(Atom::X(s)) => Some(s) arms.

Sibling soft-projection peer of Self::as_quote_form (the soft-decomposition of the four homoiconic prefix wrappers into (QuoteForm, &Sexp)) and Self::as_list (the soft-decomposition of the structural list constructor into &[Sexp]). Together the three projections (as_atom, as_list, as_quote_form) and their nullary peer (Self::Nil via matches!(self, Self::Nil)) cover every outer-shape arm of the Sexp algebra: Nil + Atom + List + 4 quote-family arms = 7 outer shapes, with the typed- projection set partitioning them by structural axis.

Composition law binding Sexp::as_X to the typed Atom algebra: for every Sexp s, s.as_symbol() (and each as_keyword / as_string / as_int / as_bool sibling) == s.as_atom().and_then(Atom::as_<variant>). The Sexp::as_float consumer specializes through the widening inline composition s.as_atom().and_then(|a| a.as_float() .or_else(|| a.as_int().map(|n| n as f64))) so the algebra-level Atom::as_float stays strict and the typed-identity distinction Int(1) vs Float(1.0) is preserved at the algebra layer (see Atom::as_int’s docstring for the discipline).

Theory anchor: THEORY.md §VI.1 — generation over composition; the six inline Self::Atom(Atom::X(s)) => Some(_) arms across the Sexp::as_X family is past the three-times rule. THEORY.md §II.1 invariant 2 — free middle; SIX consumers (as_symbol, as_keyword, as_string, as_int, as_float, as_bool) now route through ONE typed structural lift (this method) AND ONE per-variant projection family on the closed-set Atom algebra rather than six byte-identical outer-arm matches each. THEORY.md §V.1 — knowable platform; the (Sexp variant, inner payload kind) pairing becomes a TYPE projection on the substrate algebra rather than six inline arms scattered across the six Sexp::as_X consumers.

Source

pub fn as_atom_kind(&self) -> Option<AtomKind>

Soft projection onto the closed-set AtomKind atomic-payload carving marker — the 6-of-12 carving of the SexpShape algebra covering Self::Atom’s six per-payload variants (Atom::Symbol, Atom::Keyword, Atom::Str, Atom::Int, Atom::Float, Atom::Bool). Returns Some(a.kind()) iff this is a Self::Atom variant, None for every other outer shape (Self::Nil, Self::List, every quote-family wrapper: Self::Quote, Self::Quasiquote, Self::Unquote, Self::UnquoteSplice).

Direct value-level peer of the shape-level projection SexpShape::as_atom_kind — the pair (Sexp::as_atom_kind, SexpShape::as_atom_kind) binds the (Sexp value, AtomKind carving marker) pairing at ONE typed method on each algebra, closing the atomic-axis cell of the (Sexp value → carving marker) matrix. Sibling soft-projection peer of Self::as_structural_kind (the 2-of-12 residual carving returning Option<StructuralKind>) and Self::as_quote_form (the 4-of-12 quote-family carving returning Option<(QuoteForm, &Sexp)>) — post-lift ALL THREE carvings that partition the twelve outer shapes of the SexpShape algebra have a marker-only value-level projection on Sexp: as_atom_kind (atomic axis), as_quote_form (quote-family axis, marker + inner), as_structural_kind (residual axis). The Sexp::as_atom projection stays available for consumers that need the inner Atom payload for further per-variant typed projection (Atom::as_symbol et al.); this projection is the shortcut for consumers that only need the carving-marker identity.

Composition laws (dual bindings): s.as_atom_kind() == s.as_atom().map(Atom::kind) == s.shape().as_atom_kind() for every s: &Sexp. Pre-lift the atomic carving marker at the value level was reachable only via one of these two-step compositions — either through the Atom algebra (as_atom().map(Atom::kind)) or through the shape algebra (shape().as_atom_kind()). Post-lift the projection lands at ONE typed method on the value algebra, and both compositions are pinned as agreement laws (see sexp_as_atom_kind_agrees_with_as_atom_map_kind_for_every_variant and sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant in this module). A regression that drifts any of the three projections from the others surfaces immediately.

Symmetric with Self::as_structural_kind’s shape (returns just the marker, no inner-payload borrow) — where Self::as_quote_form and Self::as_unquote surface both the marker AND the wrapped inner &Sexp (because the four quote-family arms and the two substitution arms structurally carry a boxed inner value), as_atom_kind and as_structural_kind return marker-only projections (the atomic arm’s inner payload is heterogeneous across the six variants — String / i64 / f64 / bool — and the residual arms carry no or list-heterogeneous payload). Consumers that need the payload compose through Self::as_atom + Atom::as_symbol et al. (atomic axis) or Self::as_list (residual axis); this projection is the payload-agnostic carving-marker cell.

Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp variant, AtomKind carving marker) pairing becomes a TYPE projection on the substrate Sexp algebra rather than a two-step composition through either the Atom algebra or the shape algebra. A typo or swap at the value-projection site is no longer a runtime drift but a compile error against the typed projection. THEORY.md §VI.1 — generation over composition; the atomic-carving marker projection now lives on the typed Sexp algebra alongside Self::as_atom, Self::as_list, Self::as_quote_form, Self::as_unquote, Self::as_structural_kind, completing the (Sexp value → closed-set carving marker) family across ALL THREE axes (atomic + quote-family + structural-residual). THEORY.md §II.1 invariant 2 — free middle; every consumer that needs the atomic-carving marker at the value level (a future tatara-check predicate keyed on the atomic cohort, a future LSP structural-navigation filter that keys on the atomic carving, a future typed-rewriter walk over the atomic arm) binds to ONE typed method on the value algebra rather than a two-step composition.

Sibling posture across the value-level marker family — the three projections (as_atom_kind, as_quote_form, as_structural_kind) form a partition of the seven outer-shape variants of the Sexp algebra: for every s: &Sexp, EXACTLY ONE returns Some(_) (pinned by the joint sweep sexp_as_atom_kind_partitions_outer_shapes_jointly_with_as_quote_form_and_as_structural_kind in this module, sibling to the pre-existing partition sweep keyed on as_atom rather than as_atom_kind). The value-level partition-total invariant across the three carvings is the value-level peer of the shape-level partition-total invariant (sexp_shape_partition_is_total_across_atom_quote_structural_carvings in error.rs); each axis has BOTH invariants pinned.

Frontier inspiration: MLIR’s mlir::dyn_cast<AtomOp>(val) typed soft-downcast onto the atomic carving of a closed-set value algebra — the (value, typed carving marker) pairing lives at ONE typed projection on the outer value-algebra sibling. The Rust-typed peer here uses the substrate’s outer Sexp algebra with Sexp::as_atom_kind closing the atomic-carving cell of the value-level soft-projection surface, symmetric with the residual-carving projection Self::as_structural_kind and the quote-family projection Self::as_quote_form. Racket’s (atom? stx) predicate paired with (syntax->datum stx) on the atomic branch — the substrate’s as_atom_kind surfaces the typed witness (AtomKind) alongside the predicate verdict in ONE Option<AtomKind> projection.

Source

pub fn shape(&self) -> SexpShape

Project this Sexp to its closed-set SexpShape outer-shape marker — Nil → SexpShape::Nil, Atom(a) → a.kind().sexp_shape(), List(_) → SexpShape::List, and each quote-family wrapper routes through as_quote_form().map(|(qf, _)| qf.sexp_shape()). The outer-shape peer on the Sexp algebra of Atom::kind (the atomic-payload axis) and QuoteForm::sexp_shape (the quote-family axis) — completes the substrate’s Sexp-shape projection family by lifting the free-function dispatcher crate::domain::sexp_shape onto the typed Sexp algebra alongside its Atom / QuoteForm peers.

Composition law: s.shape() == crate::domain::sexp_shape(s) for every s: &Sexp. The free function continues to exist as a thin delegate (its callers in domain.rs’s diagnostic-builder paths, compile.rs’s TypeMismatch.got builder, and downstream tests route through s.shape() after this lift), so the (Sexp variant, SexpShape variant) pairing now binds at ONE inherent method on the algebra rather than at a free function domain consumers must reach into the module path to invoke.

Sibling-shape lift to the typed-EXIT projection trio on Atom ([fmt::Display for Atom], Atom::to_json, Atom::to_iac_forge_sexpr (removed)) and the typed-ENTRY classifier (Atom::from_lexeme): where the atomic-payload algebra carries its own per-variant projection family at the atomic-payload level, the Sexp algebra carries this single outer-shape projection that composes through Self::as_atom + Atom::kind (atomic axis) and Self::as_quote_form (quote- family axis) — every other arm (Nil, List) projects to its own SexpShape variant directly.

Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp variant, SexpShape variant) pairing becomes an inherent algebra projection rather than a free function in domain.rs, so the projection sits next to the rest of the typed Sexp algebra (Self::as_atom, Self::as_list, Self::as_quote_form, Self::head_symbol, Self::as_call) the substrate carries. THEORY.md §II.1 invariant 2 — free middle; every consumer that needs the outer shape (diagnostic builders at crate::domain::sexp_witness / crate::domain::missing_head_err, crate::compile’s TypeMismatch.got projection, future LSP / REPL / tatara-check typed-pattern matchers) now reaches a method on the value rather than a free function imported from domain. THEORY.md §VI.1 — generation over composition; the inline dispatch lifted to crate::domain::sexp_shape is now lifted ONE algebra level higher — from the free function to the inherent method — so a future Sexp variant lands at the algebra’s match site without a module-path indirection. A future extension (e.g. Sexp::Vector for #(...) reader syntax, Sexp::Map for {...}) extends THIS method + the SexpShape algebra + the free function’s delegation in lockstep — exhaustively checked by rustc across the Sexp match.

Frontier inspiration: MLIR’s mlir::Operation::getName() — the typed-IR operation projects through an inherent method to its closed-set name on the operation algebra; Sexp::shape is the unstructured-Rust peer on the Sexp algebra for the outer-shape projection surface, with SexpShape standing in for MLIR’s OperationName taxonomy. Racket’s (syntax-e stx) composed with a datum-prim classifier on the closed-set syntax-taxonomy projects a syntax object to its outer shape via a single primitive on the syntax algebra; Sexp::shape is the substrate’s typed-Rust peer.

Source

pub fn witness(&self) -> SexpWitness

Project this Sexp to its SexpWitness — the typed joint identity pairing the structural SexpShape with the renderable [Sexp::Display] projection in ONE owned value. The joint-identity peer on the Sexp algebra of Self::shape (the structural-shape-only projection) and [fmt::Display for Sexp] (the rendered-literal-only projection) — completes the substrate’s Sexp-projection family by lifting the free-function dispatcher crate::domain::sexp_witness onto the typed Sexp algebra alongside its Self::shape peer.

Composition law: s.witness() == crate::domain::sexp_witness(s) for every s: &Sexp. The free function continues to exist as a thin delegate (its callers in macro_expand.rs’s 8 typed-entry rejection builders, domain.rs’s missing_head_err caller + rewriter_non_list_err typed-exit builder, and downstream tests route through s.witness() after this lift), so the (Sexp variant, SexpWitness identity) pairing now binds at ONE inherent method on the algebra rather than at a free function domain consumers must reach into the module path to invoke. Body composes the two algebra-level projections — self.shape() for the structural identity, self.to_string() for the renderable identity — into ONE SexpWitness::new call. Pre-lift the dispatcher lived as a free function in domain.rs; post-lift the canonical site is the inherent method and the free function delegates (mirrors the Self::shape lift in 121bb60 exactly).

Sibling-shape lift to Self::shape (the structural-shape projection): where shape() carries the typed-shape axis on the Sexp algebra, witness() carries the JOINT typed-shape and renderable-literal axis — the typed identity an authoring tool diagnostic owes the operator AT the typed-entry or typed-exit rejection boundary. Every rejection-builder helper in macro_expand.rs that previously projected &Sexp through crate::domain::sexp_witness(_) at the variant boundary now reaches a method on the value rather than a free function imported from domain.

Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp variant, SexpWitness identity) pairing becomes an inherent algebra projection rather than a free function in domain.rs, so the projection sits next to the rest of the typed Sexp algebra (Self::shape, Self::as_atom, Self::as_list, Self::as_quote_form, Self::head_symbol, Self::as_call) the substrate carries. THEORY.md §II.1 invariant 2 — free middle; every consumer that needs the typed joint identity at a rejection-boundary slot (NonSymbolUnquoteTarget.got, SpliceOutsideList.got, NonSymbolParam.got, RestParamMissingName.got, RestParamTrailingTokens.first, OptionalParamMalformed.got, DefmacroNonSymbolName.got, DefmacroNonListParams.got, MissingHeadSymbol.got, RewriterNonList.got, future LSP / REPL / tatara-check typed-pattern matchers) now reaches a method on the value rather than a free function imported from domain. THEORY.md §VI.1 — generation over composition; the inline dispatch lifted to crate::domain::sexp_witness is now lifted ONE algebra level higher — from the free function to the inherent method — completing the Sexp-projection family alongside Self::shape. A future Sexp variant extension (e.g. Sexp::Vector for #(...) reader syntax, Sexp::Map for {...}) reaches this method through the already-lifted Self::shape + [fmt::Display for Sexp] pair — no new arm needed here.

Frontier inspiration: MLIR’s diagnostic builder pattern — op.emitOpError() << op projects the offending operation through inherent methods (getName(), print()) into ONE diagnostic value; Sexp::witness is the unstructured-Rust peer on the Sexp algebra for the joint typed-shape + renderable-literal projection surface, with SexpWitness standing in for MLIR’s InFlightDiagnostic typed payload.

Source

pub fn type_name(&self) -> &'static str

Project this Sexp to its stable, human-readable outer-shape label — the &'static str axis on the Sexp algebra. Lifts the free-function dispatcher crate::domain::sexp_type_name onto the typed Sexp algebra alongside its Self::shape / Self::witness / Self::to_json / Self::from_json sibling projections, completing the substrate’s Sexp-projection family at the canonical-label axis the way Self::shape completes the typed-shape axis and [fmt::Display for Sexp] completes the canonical-string axis.

Composition law: s.type_name() == s.shape().label() == crate::domain::sexp_type_name(s) for every s: &Sexp. Pre-lift the projection lived as a free function in domain.rs consumers (in particular the LispError::TypeMismatch got slot in compile.rs and the legacy substring-grep rejection-message tests) reached across module boundaries to invoke; post-lift the canonical site is the inherent method on the Sexp algebra and the free function delegates so existing callers continue to compile. Body composes through Self::shape + SexpShape::label so a future Sexp variant (e.g. Sexp::Vector for #(...) reader syntax, Sexp::Map for {...}) lands at one extension site (Self::shape’s exhaustive arm) rather than a parallel &'static str match — the projection is structurally derived, not duplicated.

Sibling-shape lift to Self::shape (the typed-shape projection): where shape() carries the typed SexpShape identity (matchable, exhaustive across Sexp variants), type_name() carries the &'static str literal the rendered diagnostic surface wants (still derived from the typed identity, but flattened through SexpShape::label for substring-grep callers and the TypeMismatch.got slot). The &'static str lifetime makes the projection cheap to embed in any error variant without allocation.

Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp variant, &'static str label) pairing becomes an inherent algebra projection rather than a free function in domain.rs, so the projection sits next to the rest of the typed Sexp algebra (Self::shape, Self::witness, Self::to_json, Self::from_json, Self::as_atom, Self::as_list, Self::as_quote_form, Self::head_symbol, Self::as_call) the substrate carries. THEORY.md §II.1 invariant 2 — free middle; every consumer that needs the outer-shape label (LispError::TypeMismatch.got projection in compile.rs, legacy substring-grep rejection-message tests, future LSP / REPL diagnostic surfaces) now reaches a method on the value rather than a free function imported from domain. THEORY.md §VI.1 — generation over composition; the inline s.shape().label() recipe lifted to crate::domain::sexp_type_name is now lifted ONE algebra level higher — from the free function to the inherent method — completing the Sexp-projection family alongside Self::shape / Self::witness / Self::to_json / Self::from_json. The domain.rs sexp_* free-function namespace is now structurally reserved for free functions that genuinely need a domain-module reach (registry dispatch, kwargs gates, registry suggestions), not algebra-layer projections.

Frontier inspiration: MLIR’s mlir::Operation::getName() composed with OperationName::getStringRef() — the typed-IR operation projects through inherent methods to its closed-set label on the operation algebra; Sexp::type_name is the unstructured-Rust peer on the Sexp algebra for the canonical-label projection surface, with SexpShape::label standing in for MLIR’s OperationName::getStringRef second hop. Racket’s (syntax-name stx) — the typed inverse of (syntax-e stx) on the syntax algebra; Sexp::type_name composes the typed-shape projection with its closed-set label projection at the inherent-method site rather than the typeclass-method site, matching pleme-io’s “rust-typed, not trait-typed” idiom for closed-set algebras.

Source

pub fn to_json(&self) -> Result<Value, LispError>

Project this Sexp to its canonical serde_json::Value rendering — the typed-algebra peer of Atom::to_json at the Sexp layer. Lifts the free-function dispatcher crate::domain::sexp_to_json onto the typed Sexp algebra alongside its Self::shape / Self::witness sibling projections, completing the JSON-projection axis at the algebra layer the way [fmt::Display for Sexp] completes the canonical-string axis. The free function continues to exist as a thin delegate (its callers in tatara-lisp-derive’s derive output route through it via the crate::domain::sexp_to_json import); the from_value_with_path private helper in domain.rs and the recursive sub-calls inside this method route through the inherent method directly so the canonical-site indirection disappears at every internal callsite.

Rules (preserve byte-identical pre-lift behavior at the sexp_to_json callsite):

Composition law: s.to_json() == crate::domain::sexp_to_json(s) for every s: &Sexp. Pre-lift the dispatcher lived as a free function in domain.rs; post-lift the canonical site is the inherent method and the free function delegates (same lift posture as Self::shape in 121bb60 and Self::witness in a427e3b).

Sibling-shape lift to Self::shape (the structural-shape projection), Self::witness (the joint structural-shape + renderable-literal projection), and [fmt::Display for Sexp] (the renderable-literal projection): where those three carry the Lisp-canonical-form / structural-identity axes on the algebra, to_json carries the JSON canonical-form axis. The substrate’s Sexp algebra now binds ALL THREE canonical-form projection surfaces (Lisp Display, JSON, and the feature-gated iac-forge From<&Sexp> for SExpr) at the algebra layer, with per-variant atomic rendering composed through the corresponding Atom projection family (Atom::Display, Atom::to_json, Atom::to_iac_forge_sexpr).

Theory anchor: THEORY.md §VI.1 — generation over composition; the inline dispatch the prior runs lifted onto crate::domain::sexp_to_json (the free function) is now lifted ONE algebra level higher — from the free function to the inherent method — completing the Sexp-projection family alongside Self::shape and Self::witness. THEORY.md §II.1 invariant 2 — free middle; the typed-exit JSON projection (every consumer that round-trips a Sexp through serde_json::from_value::<T> for typed-domain deserialization, the typed-rewriter at [crate::domain::TypedRewriter], the derive macro’s compile_from_args fallthrough, and any future canonical- form surface) all route through ONE inherent algebra method rather than reach into the domain module path for a free function. THEORY.md §V.1 — knowable platform; a future Sexp variant extension (e.g. Sexp::Vector for #(...) reader syntax, Sexp::Map for {...}) reaches this method through the already-lifted Self::as_quote_form + Atom::to_json pair — one arm added here for the new outer-shape variant; rustc enforces the per-variant body is named.

Frontier inspiration: MLIR’s mlir::AsmPrinter::printOp — the typed-IR printer dispatches on the closed-set Op so every printer body for an op lives at ONE implementation site; Sexp::to_json is the unstructured-Rust peer on the Sexp algebra for the JSON canonical-form surface (where [fmt::Display for Sexp] is the Lisp-canonical-form peer and From<&Sexp> for iac_forge::SExpr is the canonical-attestation-form peer). Racket’s (syntax->datum stx) then a serializer over the datum prim — to_json is the substrate’s serializer at the Sexp layer composed through Atom::to_json at the atomic-payload layer, with the closed-set AtomKind standing in for Racket’s datum-prim taxonomy.

Source

pub fn from_json(v: &Value) -> Sexp

Inverse of Self::to_json — project a serde_json::Value back onto a Sexp. The closed-set serde_json::Value discriminator maps directly onto the corresponding Sexp constructor:

  • serde_json::Value::NullSelf::Nil.
  • serde_json::Value::BoolSelf::boolean.
  • serde_json::Value::NumberSelf::int when the value fits an i64, otherwise Self::float when it fits an f64; the structural impossibility “neither i64 nor f64” collapses to Self::int(0) as a typed floor — serde_json::Number’s closed-set discriminator excludes this case in practice (every serde_json::Number is either i64-fitting, u64-fitting projected through f64, or f64-fitting directly), but the typed floor stays explicit so a future serde_json extension does not silently misroute. Mirror of Atom::to_json’s Self::int / Self::float bifurcation.
  • serde_json::Value::StringSelf::string. The serde_json::Value::String discriminator is type-erased — a serde-projected symbol AND a serde-projected keyword AND a genuine string literal ALL inhabit it on the JSON side — so the back-projection chooses Self::string as the lossless floor for the Atom::Symbol / Atom::Keyword / Atom::Str three-way collapse. Consumers that need the symbol-vs-string distinction must preserve it BEFORE the JSON round-trip (e.g. through a typed enum’s serde projection rather than a raw Sexp-to-JValue round-trip).
  • serde_json::Value::ArraySelf::List mapping each element through this method recursively.
  • serde_json::Value::ObjectSelf::List of alternating :key value pairs in serde_json::Map’s iteration order (sorted by key under serde_json’s default BTreeMap backing; insertion order under the optional preserve_order feature, which the substrate does NOT enable today), with each JSON key projected through [crate::domain::camel_to_kebab] to recover the :k’s kebab-case authoring shape and each JSON value recursed through this method. Inverse of Self::to_json’s Self::List kwargs-shape arm: that arm projects :k v :k v … into a JSON object via [crate::domain::kebab_to_camel]; this arm projects the object back into a Self::List of alternating keyword / value via the inverse [crate::domain::camel_to_kebab].

Composition law: Self::from_json(&s.to_json()?) projects back to a Sexp whose Self::to_json re-projection produces the SAME JValue (modulo the lossy Symbol / Keyword / Str three-way collapse documented above; for the round-trippable subset, Sexp::Nil, the six Atom kinds within their discriminator class, and recursively Sexp::List of round- trippable elements, the law holds byte-for-byte).

Sibling-lift posture: this method mirrors the prior crate::domain::sexp_to_jsonSelf::to_json (commit 875ee3b) / crate::domain::sexp_shapeSelf::shape (commit 121bb60) / crate::domain::sexp_witnessSelf::witness (commit a427e3b) family of lifts, all of which promoted a free function in domain.rs to the inherent-method canonical site on the Sexp algebra. Pre-lift the json_to_sexp dispatcher lived in domain.rs as the canonical site; post-lift this inherent method is the canonical site and the free function delegates so every existing caller continues to compile.

Sibling-shape lift on the round-trip closure: the substrate’s Sexpserde_json::Value round-trip now lives entirely as two inherent methods on the Sexp algebra — Self::to_json (forward) and Self::from_json (inverse). Consumers that previously round-tripped a typed value through Lisp forms via domain::sexp_to_json + domain::json_to_sexp now bind to ONE algebra (the inherent-method family) rather than reaching across the domain module path for two free functions. A future canonical-form surface (e.g., a YAML round-trip via [serde_yaml], a Nix-expression round-trip via the typed Nix surface in tatara-nix) hangs off the SAME Sexp algebra at Self::to_yaml / Self::from_yaml / Self::to_nix / Self::from_nix — the naming pattern is now structurally established by this pair.

Theory anchor: THEORY.md §VI.1 — generation over composition; the inline json_to_sexp dispatcher in domain.rs is lifted ONE algebra level higher (from free function to inherent method), completing the Sexp ↔ JValue round-trip closure alongside Self::to_json. THEORY.md §V.1 — knowable platform; the inverse projection becomes a NAMED primitive on the substrate’s Sexp algebra rather than a domain-module free function consumers reach across module boundaries to call. THEORY.md §II.1 invariant 2 — free middle; every consumer that round-trips through JSON (the typed-rewriter at [crate::domain::TypedRewriter], the derive macro’s compile_from_args JSON fallthrough, the test round-trip fixtures) routes through ONE inherent algebra method — the typed round-trip closure is structurally complete on the Sexp algebra.

Frontier inspiration: MLIR’s mlir::parseAttribute(str, ctx) — the typed-IR parser inverse of printAttribute lives on the same Attribute algebra as its printer dual; the substrate’s Self::from_json is the unstructured-Rust peer on the Sexp algebra for the JSON canonical-form inverse, paired with Self::to_json as the closed round-trip. Racket’s (datum->syntax stx datum) — the round-trip inverse of (syntax->datum stx), projected at the datum algebra layer; Self::from_json is the substrate’s peer at the Sexp layer (one algebra level lower than Racket’s syntax wrapper).

Source

pub fn as_symbol(&self) -> Option<&str>

Source

pub fn as_keyword(&self) -> Option<&str>

Source

pub fn as_string(&self) -> Option<&str>

Source

pub fn as_int(&self) -> Option<i64>

Source

pub fn as_float(&self) -> Option<f64>

Some(f) for Atom::Float(f), AND Some(n as f64) for Atom::Int(n) — caller convenience at the numeric-kwarg boundary. The Int-widening face lives at this consumer layer rather than at Atom::as_float (strict per the typed-identity discipline pinned at Atom::as_int’s docstring); the typed soft-projection algebra on Atom stays strict, and the Sexp::as_float consumer composes the strict typed projection with a fallback widening branch on Atom::as_int.

Source

pub fn as_bool(&self) -> Option<bool>

Source

pub fn as_symbol_or_string(&self) -> Option<&str>

foo or "foo" — useful for names that may be authored either way.

Structural-lift composition: routes through Sexp::as_atom + the algebra-level Atom::as_symbol_or_string union projection — the same as_atom().and_then(Atom::as_X) composition pattern Sexp::as_symbol / Sexp::as_keyword / Sexp::as_string / Sexp::as_int / Sexp::as_bool route through on the per-variant axis. Lifts the disjunctive self.as_symbol().or_else(|| self.as_string()) composition at this site’s pre-lift body (TWO Sexp::as_atom traversals — one per per-variant projection) onto ONE typed-algebra union projection reached via ONE Sexp::as_atom traversal.

Composition law: s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string) for every Sexp s. See Atom::as_symbol_or_string for the algebra-level peer’s docstring (per-variant family completion + theory grounding).

Source

pub fn head_symbol(&self) -> Option<&str>

The symbol in operator position — Some(s) iff this is a non-empty list whose first element is a symbol ((defpoint …)Some("defpoint")). None for every other shape: a non-list (foo, 5, :kw), the empty list (), and a list whose head is not a symbol ((5 …), (:kw …), ((nested) …)).

This is the operator-position projection — the structural query every form-dispatch site in the substrate keys on: “what operator does this form invoke?” Macroexpansion (Expander::expand looks up the head against the macro table; macro_def_from reads it to recognize a defmacro head) and the typed compilers (compile_typed / compile_named_from_forms match it against T::KEYWORD) all asked the same self.as_list()?.first()?.as_symbol() question inline. Naming it once makes “operator position” a primitive of the Sexp algebra rather than four byte-identical inline chains.

This is the SOFT face of operator-position dispatch — it answers “is this form an invocation of some operator?” and yields None (skip / fall through) for everything that isn’t, with no diagnostic. Its STRICT sibling is TataraDomain::compile_from_sexp, which on a matched-arity form distinguishes the empty-list and present-but-not-a-symbol head sub-modes to emit a rich MissingHeadSymbol rejection. The two are the dispatch (head_symbol) and the gate (compile_from_sexp) faces of the same projection; keeping both lets a site choose “skip silently” or “reject loudly” without re-deriving the head.

head_symbol is the operator projection of Sexp::as_call: it keeps the head and discards the argument tail. The as_list()?.first()?.as_symbol() chain lives in ONE place (as_call); this is its first component.

Source

pub fn as_call(&self) -> Option<(&str, &[Sexp])>

Decompose a call form into its operator and argument tail — Some((op, args)) iff this is a non-empty list whose first element is a symbol, where op is that head symbol and args is the remaining elements (&self[1..], possibly empty). None for every shape head_symbol rejects: a non-list, the empty list, and a list whose head is present but not a symbol.

This is the call-form decomposition — the structural shape of a Lisp invocation: an operator applied to an argument tail. It pairs the operator-position projection (head_symbol) with the argument tail every dispatch site reads immediately after matching the operator. Macroexpansion (Expander::expand) applies the matched macro to &list[1..]; the typed compilers (compile_typed, compile_named_from_forms) feed &list[1..] into T::compile_from_args. Before this query each site bound as_list() for the tail AND independently called head_symbol() (which itself re-derives as_list().first()) for the operator — two traversals of the same list, two projections. as_call yields both from one match, so the operator and its arguments can never drift out of agreement at a dispatch site.

Soft face, like head_symbol: it answers “is this an invocation of some operator, and what are its arguments?” and yields None (skip / fall through) for everything that isn’t, with no diagnostic. The strict gate sibling is TataraDomain::compile_from_sexp, which distinguishes the empty-list and non-symbol-head sub-modes to reject loudly.

Source

pub fn call<H, I>(head: H, args: I) -> Sexp
where H: Into<String>, I: IntoIterator<Item = Sexp>,

Canonical call-form outer constructor — composes the atomic- payload construct family’s Self::symbol (the head-position construct on the 6-of-12 atomic carving of SexpShape) with the residual-axis construct family’s Self::list (via std::iter::once(head_sexp).chain(args)) to build a symbol- headed list-shaped Sexp value at ONE site on the closed-set Sexp algebra. The call-form section-for-retraction sibling of the existing Self::as_call soft-projection ([Option<(& str, &[Sexp])>]): where the projection soft-decomposes a symbol-headed list into its head symbol and argument tail, this constructor embeds a fresh (head string, item sequence) pair into the matching call-shaped wrapper.

Composition sibling of the atomic-payload construct family (Self::symbol, Self::keyword, Self::string, Self::int, Self::float, Self::boolean — routing through the typed Atom family on the 6-of-12 atomic carving), the quote-family construct family (Self::quote, Self::quasiquote, Self::unquote, Self::unquote_splice — routing through the typed QuoteForm::wrap family on the 4-of-12 quote-family carving), and the residual-axis construct Self::list (routing owned or iterable item sequences into the tuple-variant on the 2-of-12 residual carving): those close the (construct, project) algebra dual on their respective STRUCTURAL carvings; this closes the (construct, project) algebra dual on the SYMBOL-HEADED-LIST TYPED DECOMPOSITION — the load-bearing shape every Lisp invocation, every (defX …) typed-domain call form, and every macroexpander template head takes on the outer Sexp algebra.

Composition law (forward, through the outer algebra’s atomic + residual construct families): Sexp::call(head, args) == Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args)) for every head: impl Into<String> + args: impl IntoIterator<Item = Sexp>. The body binds through the SAME two construct methods consumers already reach for when threading a head-then-rest sequence into a call form — the composition law lifts that two-method inline pattern to ONE named query on the outer Sexp algebra.

Round-trip law (section-for-retraction with the soft-projection sibling): for every head: &str + args: Vec<Sexp>, Sexp::call(head, args.clone()).as_call() == Some((head, args.as_slice())) — the outer algebra’s call-form typed constructor pairs section-for-retraction with the outer algebra’s soft call-form projection, and the (head symbol, args slice) cross-projection preserves identity. Keyword- matched round-trip law: for every head: &str + args: Vec<Sexp>, Sexp::call(head, args.clone()).as_call_to(head) == Some(args.as_slice()) — the keyword-typed projection recovers the args tail iff its argument keyword matches the constructor’s head. Head-symbol composition law: Sexp::call(head, args).head_symbol() == Some(head.as_str()) for every head: impl Into<String> + args: impl IntoIterator<Item = Sexp> — the head-position projection recovers the constructor’s head byte-for-byte.

Outer-shape composition law: Sexp::call(head, args).shape() == SexpShape::List for every input — a call form is a list-shaped Sexp, and the outer-shape identity binds through the typed- shape lattice at the residual arm. Structural-carving-marker composition law: Sexp::call(head, args).as_structural_kind() == Some(StructuralKind::List) — the residual-axis carving marker binds through the closed-set StructuralKind algebra at ONE arm, symmetric with the atomic-axis’s Sexp::X_atom( payload).as_atom_kind() == Some(AtomKind::X) marker composition.

Pre-lift the Sexp::List(std::iter::once(Sexp::symbol(head)) .chain(args).collect()) composition (or equivalently the Sexp::List(vec![Sexp::symbol(head), args...]) welded triple) appeared inline at every consumer that builds a call-shaped Sexp value — well past the ≥2 PRIME-DIRECTIVE trigger once the call-form shape is named. Post-lift consumers that have a head string + an owned or iterable sequence of args bind to ONE typed-algebra method on the outer Sexp algebra with the impl Into<String> bound on the head absorbing &str / String / &String and the impl IntoIterator<Item = Sexp> bound on the args absorbing Vec<Sexp> / [Sexp; N] / .map(...) chains without a per-site .collect::<Vec<Sexp>>() coercion.

Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the (head string, args sequence, Self::List tuple-variant constructor) triple binds at ONE typed-algebra method on the outer Sexp algebra, closing the call-form (construct, project) algebra dual pair with Self::as_call / Self::as_call_to / Self::head_symbol. THEORY.md §II.1 invariant 2 — free middle; every consumer that has a head string + an owned or iterable sequence of args and wants to build a call-shaped Sexp routes through the SAME typed method, so a regression that drifts one consumer’s construction from the others (e.g. a copy-edit that emits Sexp::keyword( head) for the head position, or that swaps in a Sexp::string head that Self::as_call then rejects at the projection site) cannot reach the substrate’s runtime. THEORY.md §V.1 — knowable platform; the call-form typed-construct becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed-project family Self::as_call / Self::as_call_to rather than bare tuple-variant constructor paired with per-site Sexp::List(vec![Sexp::symbol(...), ...]) discipline. THEORY.md §VI.1 — generation over composition; the call-form pair emerges from ONE typed-algebra composition through Self::list composed with Self::symbol rather than from per-consumer per-callsite literals; a future call- form shape extension (e.g. a keyword-headed call form for a Kernel-style applicative-vs-operative split) lands as ONE peer constructor on this algebra alongside the residual, quote- family, and atomic-payload construct families.

Source

pub fn named_call<H, N, I>(head: H, name: N, spec_args: I) -> Sexp
where H: Into<String>, N: Into<String>, I: IntoIterator<Item = Sexp>,

Canonical named-call-form outer constructor — composes the call- form typed constructor Self::call with the atomic-payload construct family’s Self::symbol (for the NAME slot) via std::iter::once(Self::symbol(name)).chain(spec_args) to build a (head NAME spec_args…) symbol-headed named list-shaped Sexp value at ONE site on the closed-set Sexp algebra. The named- call-form section-for-retraction sibling of the existing Self::as_named_call_to soft-projection ([Option<crate::error:: Result<(&str, &[Sexp])>>]): where the projection soft-decomposes a (<keyword> NAME spec_args…) symbol-headed list into its NAME symbol and spec args tail through the named-form gate (crate::compile::split_name_slot), this constructor embeds a fresh (head string, name string, spec_args sequence) triple into the matching named-call-shaped wrapper. Composition sibling of the call-form construct Self::call on the outer algebra: where Self::call closes the (construct, project) dual on the CALL-FORM TYPED DECOMPOSITION ((head args…)), this closes the dual on the NAMED-CALL-FORM TYPED DECOMPOSITION ((head NAME spec_args…)) — the load-bearing shape every (defX NAME …) typed-domain named authoring form takes on the outer Sexp algebra, and the section-for-retraction dual of the crate::compile::split_name_slot gate at the value level.

Composition law (forward, through the call-form + atomic-payload construct families): Sexp::named_call(head, name, spec_args) == Sexp::call(head, std::iter::once(Sexp::symbol(name)).chain( spec_args)) for every head: impl Into<String> + name: impl Into<String> + spec_args: impl IntoIterator<Item = Sexp>. The body binds through the SAME two construct methods consumers already reach for when threading a head-then-name-then-rest sequence into a named call form — the composition law lifts that two-method inline pattern to ONE named query on the outer Sexp algebra.

Round-trip law (section-for-retraction with the named-form soft- projection): for every head: &'static str + name: &str + spec_args: Vec<Sexp>, Sexp::named_call(head, name, spec_args .clone()).as_named_call_to(head) == Some(Ok((name, spec_args .as_slice()))) — the outer algebra’s named-call-form typed constructor pairs section-for-retraction with the outer algebra’s soft named-call-form projection, and the (head symbol, NAME symbol, spec args slice) cross-projection preserves identity. Call-form projection composition: Sexp::named_call( head, name, spec_args).as_call() == Some((head, once(Sexp::symbol(name)).chain(spec_args).collect().as_slice() )) — the call-form soft-projection recovers (head, [name, spec_args…]) with the NAME symbol as the first arg, mirroring the Self::call round-trip on the encompassing call algebra. Keyword-matched round-trip law: for every head: &'static str + name: &str + spec_args: Vec<Sexp>, Sexp::named_call(head, name, spec_args.clone()).as_call_to(head) == Some( [Sexp::symbol(name), spec_args…].as_slice()) — the keyword- typed projection recovers the NAME-headed args tail iff its argument keyword matches the constructor’s head. Head-symbol composition law: Sexp::named_call(head, name, spec_args) .head_symbol() == Some(head.as_str()) — the head-position projection recovers the constructor’s head byte-for-byte.

Outer-shape composition law: Sexp::named_call(head, name, spec_args).shape() == SexpShape::List for every input — a named call form is a list-shaped Sexp, the outer-shape identity binds through the typed-shape lattice at the residual arm. Structural-carving-marker composition law: Sexp:: named_call(head, name, spec_args).as_structural_kind() == Some(StructuralKind::List) — the residual-axis carving marker binds through the closed-set StructuralKind algebra at ONE arm, symmetric with Self::call’s residual-arm marker composition.

Named-form gate composition law: crate::compile::split_name_slot( &Sexp::named_call(head, name, spec_args).as_call_to(head) .unwrap(), head) == Ok((name, spec_args.as_slice())) — the substrate’s named-form arity + NAME-shape gate accepts every output of this constructor byte-for-byte, closing the section- for-retraction pair at the gate level as well as at the projection level. A constructor emission that drifts into a missing-NAME shape (empty spec_args yields (head), which the call-form projection recovers but the named-form gate rejects with NamedFormMissingName) or a non-symbol-NAME shape (Sexp::keyword(name) for the NAME position, which the gate rejects with NamedFormNonSymbolName) becomes structurally impossible — the impl Into<String> NAME bound admits string payloads only, and the Self::symbol wrap routes to the symbol atom variant as_symbol_or_string accepts.

Pre-lift the Sexp::call(head, std::iter::once(Sexp::symbol( name)).chain(spec_args)) composition (or equivalently the Sexp::List(vec![Sexp::symbol(head), Sexp::symbol(name), spec_args...]) welded quadruple) appeared inline at every consumer that builds a (defX NAME …)-shaped Sexp value — well past the ≥2 PRIME-DIRECTIVE trigger once the named call-form shape is named. Post-lift consumers that have a head string + a NAME string + an owned or iterable sequence of spec args bind to ONE typed-algebra method on the outer Sexp algebra with the two impl Into<String> bounds absorbing &str / String / &String on both string positions and the impl IntoIterator<Item = Sexp> bound on the spec args absorbing Vec<Sexp> / [Sexp; N] / .map(...) chains without a per-site .collect::<Vec<Sexp>>() coercion.

Frontier inspiration: Racket’s syntax-parse (~datum keyword) name:id spec ... pattern binds the NAME slot through the name:id capture binder and consumers reference it downstream; the constructor peer on the same surface is syntax-e composed with datum->syntax wrapping a (list #'keyword name-id spec-list ...) triple. Sexp:: named_call is the unstructured-Rust peer — a section-for- retraction constructor on the outer algebra that mirrors the ~datum keyword name:id spec ... pattern’s NAME capture on the construct side. Tree-sitter’s query-matched named captures have the same shape on the tree side: the query pattern binds a NAME capture, the constructor peer (ts_node_new composed with ts_node_field_set) embeds a fresh NAME child at the corresponding field slot. The typed structural rejection chain the substrate’s named-form gate emits (crate::error::LispError::NamedFormMissingName, crate::error::LispError::NamedFormNonSymbolName) is preserved by construction — the constructor cannot emit a value the gate rejects, symmetric with the ~datum / name:id reader-side rejection that fires BEFORE any downstream binding sees the drifted shape.

Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the (head string, NAME string, spec args sequence, Self::call call-form constructor) quadruple binds at ONE typed-algebra method on the outer Sexp algebra, closing the named-call- form (construct, project) algebra dual pair with Self::as_named_call_to / Self::as_named_call_to_any on the projection side and crate::compile::split_name_slot on the gate side. THEORY.md §II.1 invariant 2 — free middle; every consumer that has a head + NAME + spec args and wants to build a named-call-shaped Sexp routes through the SAME typed method, so a regression that drifts one consumer’s construction from the others (e.g. a copy-edit that emits Sexp::keyword(name) for the NAME position, which the named-form gate would reject with NamedFormNonSymbolName) cannot reach the substrate’s runtime. THEORY.md §V.1 — knowable platform; the named-call-form typed-construct becomes a TYPE projection on the substrate’s outer Sexp algebra sitting next to the typed-project family [Self:: as_named_call_to] / Self::as_named_call_to_any + crate::ast::iter_named_calls_to / crate::ast::iter_named_calls_to_any rather than a per-site inline composition. THEORY.md §VI.1 — generation over composition; the named-call-form pair emerges from ONE typed- algebra composition through Self::call composed with Self::symbol rather than from per-consumer per-callsite literals; a future named-form shape extension (e.g. a dotted-NAME form, or a typed-NAME form where the NAME slot carries a compile-time-decoded typed witness) lands as ONE peer constructor on this algebra alongside the call-form, residual, quote-family, and atomic-payload construct families.

Source

pub fn as_call_to(&self, keyword: &str) -> Option<&[Sexp]>

Decompose a call form into its argument tail IFF the head matches the supplied keywordSome(args) iff this is a non-empty list whose first element is a symbol equal to keyword, where args is the remaining elements (&self[1..], possibly empty). None for every shape as_call rejects AND for every call whose head is present but differs from keyword.

This is the keyword-typed call decomposition — the natural extension of Sexp::as_call for the “is this a call to ONE specific operator?” question every typed-domain dispatch site asks after macroexpansion. compile_typed and compile_named_from_forms both opened the same two-step chain inline — if let Some((head, args)) = form.as_call() { if head == T::KEYWORD { … } } — at every form they walked; the chain IS this projection. Naming it lifts “is this form a call to T?” from a two-step inline pattern to ONE structural query on the Sexp algebra. A regression that drifts one consumer’s comparison from == to != , or that compares against a different label than T::KEYWORD (e.g. substring-grepping the rendered head), becomes structurally impossible: there is exactly one implementation both dispatchers route through.

Soft face, like the rest of the as_* family: it answers “is this a call to keyword, and what are its arguments?” and yields None for everything that isn’t (skip / fall through), with no diagnostic. The strict gate sibling is TataraDomain::compile_from_sexp, which distinguishes the not-a-list / empty-list / non-symbol-head / wrong-keyword sub-modes to reject loudly. The two are the dispatch (as_call_to) and the gate (compile_from_sexp) faces of the same projection; keeping both lets a site choose “skip silently” or “reject loudly” without re-deriving the head.

Structural identity binding it to its siblings:

  • as_call_to(keyword) == as_call().and_then(|(h, args)| (h == keyword).then_some(args))
  • as_call_to(keyword).is_some() == (head_symbol() == Some(keyword))

The returned &[Sexp] borrows from the list’s tail verbatim — no copy, no allocation, same lifetime as Sexp::as_call’s tail.

Slice-side sibling: iter_calls_to lifts this per-form projection onto a &[Sexp], yielding the args slices of every matching form in source order — the substrate’s typed-keyword filter over a batch of forms, structurally bound to this per-form projection via the closed-form composition iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k)).

Source

pub fn as_call_to_any<F, T>(&self, decode: F) -> Option<(T, &[Sexp])>
where F: FnOnce(&str) -> Option<T>,

Decompose a call form whose head decodes through a caller-supplied classifier — Some((decoded, args)) iff this is a non-empty list whose first element is a symbol AND decode(head) returns Some(decoded), where args is the remaining elements (&self[1..], possibly empty). None for every shape Sexp::as_call rejects AND for every call whose head is present but decode rejects.

This is the typed-decoded call decomposition — the closure-typed extension of Sexp::as_call_to for the “is this a call whose head belongs to a CLOSED SET (or a LIVE REGISTRY) that decodes to a typed witness?” question. Where Sexp::as_call_to filters by ONE constant keyword, as_call_to_any filters AND TYPES by a caller- supplied projection — every dispatch site that asks “is this form an invocation of any of N operators, decoded as a typed enum or resolved against a runtime table?” binds to ONE structural query on the Sexp algebra. Two consumers route through it:

  • The macro-expander’s macro_def_from — closed-set classifier: as_call_to_any(MacroDefHead::from_keyword) decides which of {defmacro, defpoint-template, defcheck} a top-level form invokes, decoded to the typed MacroDefHead enum. Pre-lift the site opened the same three-step chain inline — let Some(list) = form.as_list()…; let Some(head) = form.head_symbol()…; let Some(decoded) = MacroDefHead::from_keyword(head)….
  • The macro-expander’s Expander::expand — live-registry classifier: as_call_to_any(|h| self.macros.get(h)) decides which of the registered macros (a HashMap<String, MacroDef> populated by expand_program’s defmacro recognition) a form invokes, decoded to &MacroDef. Pre-lift the site opened the same as_list() + as_call() + self.macros.get(head) chain inline — as_list() for the children-walk fallthrough, as_call() for the (head, args) pair (which itself re-derives as_list() internally), and self.macros.get(head) for the registry lookup.

Naming the projection lifts “is this form a call to any of N operators, decoded to T?” from the three-step inline pattern to ONE structural query — closed-set enum classifier OR live-registry HashMap classifier, the family primitive is uniform under both.

Soft face, like the rest of the as_* family: it answers “is this a call whose head decodes through F, and what are its arguments?” and yields None for everything that isn’t (skip / fall through), with no diagnostic. The strict gate sibling stays TataraDomain::compile_from_sexp — that distinguishes the not-a-list / empty-list / non-symbol-head / wrong-keyword sub-modes to reject loudly for a single-keyword consumer. The two are the closed-set-decoded dispatch (as_call_to_any) and the single-keyword gate (compile_from_sexp) faces of the typed-domain recognition problem; keeping both lets a site choose “skip silently if the head isn’t ours” or “reject loudly if the head isn’t the exact keyword” without re-deriving the head.

Structural identity binding it to its siblings:

  • as_call_to_any(decode) == as_call().and_then(|(h, args)| decode(h).map(|d| (d, args)))
  • as_call_to(k) == as_call_to_any(|h| (h == k).then_some(())).map(|(_, a)| a) (modulo the discarded ())
  • as_call_to_any(decode).is_some() == as_call().map_or(false, |(h, _)| decode(h).is_some())

The returned &[Sexp] borrows from the list’s tail verbatim — no copy, no allocation, same lifetime as Sexp::as_call’s tail. T is owned because decode is FnOnce(&str) -> Option<T> and a &'_ str borrow into the head symbol would not outlive the helper boundary; consumers projecting to a typed Copy enum (e.g. MacroDefHead) get the value directly, consumers projecting to a borrowed &'static str (a closed-set head) project to &'static str and inherit the static lifetime through the classifier.

Slice-side sibling: iter_calls_to_any lifts this per-form projection onto a &[Sexp], yielding the (decoded, &[Sexp]) pair of every matching form in source order — the substrate’s typed-decoded filter over a batch of forms, structurally bound to this per-form projection via the closed-form composition iter_calls_to_any(forms, decode) == forms.iter().filter_map(|f| f.as_call_to_any(&mut decode)). The slice-side primitive promotes the closure constraint from FnOnce (per-form, one call per invocation) to FnMut (slice-side, one call per element) so a decoder that captures mutable state (a counter, a registry cache) maintains state across the batch walk.

Source

pub fn as_named_call_to_any<F, T>( &self, decode: F, ) -> Option<Result<(T, &str, &[Sexp]), LispError>>
where F: FnOnce(&str) -> Option<(T, &'static str)>,

Decompose a named call form (a (<keyword> NAME :k v …) shape) whose head decodes through a caller-supplied classifier — Some(Ok((decoded, name, spec_args))) iff this is a non-empty list whose first element is a symbol AND decode(head) returns Some((decoded, kw)) AND the remaining elements split cleanly into a NAME slot (symbol or string at position 1) and a spec args tail (position 2..), Some(Err(…)) iff the head decodes but the NAME slot is missing ([LispError::NamedFormMissingName]) or non-symbol-or-string ([LispError::NamedFormNonSymbolName]), None for every shape Sexp::as_call_to_any rejects AND for every call whose head is present but decode returns None for.

This is the per-form named-classifier projection — the per-form peer of iter_named_calls_to_any on the slice algebra and of crate::macro_expand::Expander::expand_and_collect_named_calls_to_any on the expander surface. Closes the (per-form × classifier × named) corner of the soft-dispatch cube the substrate’s per-form algebra (as_call_to{,_any}) and slice algebra (iter_calls_to{,_any}, iter_named_calls_to{,_any}) collectively shape — pre-lift the cube at the per-form × named corner was “(composed inline at each named consumer)” (the documented gap the cube table inside iter_named_calls_to_any called out), post-lift the per-form × named row binds to ONE primitive every per-form named consumer composes through:

bare-kwargsnamed NAME-then-kwargs
per-formSexp::as_call_to_anyas_named_call_to_any (this)
sliceiter_calls_to_anyiter_named_calls_to_any
expanderexpand_and_collect_calls_to_anyexpand_and_collect_named_calls_to_any

The slice-side iter_named_calls_to_any now routes through THIS per-form primitive via the SAME forms.iter().filter_map(_) skeleton iter_calls_to_any uses to route through Sexp::as_call_to_any, so a regression that drifts ONE row’s instrumentation, span-aware borrow walker, or fused-iterator invariant from the bare row to the named row (or vice versa) is structurally impossible.

Composes Sexp::as_call_to_any with crate::compile::split_name_slot: the classifier filter precedes the named gate, mirroring how split_name_slot is composed AFTER the classifier-decoded args tail is already in hand inside iter_named_calls_to_any. Decoder signature FnOnce(&str) -> Option<(T, &'static str)> pairs the typed witness T with the canonical static keyword threaded through the NamedFormMissingName.keyword / NamedFormNonSymbolName.keyword slots of the named-form gate — the &'static constraint pins the same compile-time discipline crate::compile::split_name_slot’s keyword: &'static str parameter pins at the slice-side boundary, AND that the slice-side decoder signature pins on the slice algebra.

Three-arm result shape — Option<Result<…>> — preserves both the classifier filter face (None for “not our head, skip silently”, matching the per-form soft-projection posture of every other as_* method on Sexp) AND the named gate face (Err for “matched head but malformed NAME”, surfacing the typed structural-rejection variants LispError::NamedFormMissingName / LispError::NamedFormNonSymbolName the slice-side and expander- surface consumers already short-circuit on). A consumer that wants “fold over every per-form result, short-circuiting on the first malformed NAME” composes .transpose() (yielding Result<Option<…>>) and ?-routes the outer Result; a consumer that wants “skip every non-matching form AND every malformed matched form” composes .and_then(|res| res.ok()) (yielding Option<(T, &str, &[Sexp])>); a consumer that wants the raw three-arm shape pattern-matches directly.

Two plausible future consumer shapes the per-form named-classifier projection admits with no boilerplate:

  • LSP hover tooltip — an authoring tool that surfaces a tooltip on the symbol under the cursor wants to ask “is THIS form (the one I just resolved to under the cursor) a named call to any registered domain, decoded to a typed kind, with the borrowed NAME slot extracted for the tooltip body?”. Pre- lift the tool would re-derive form.as_call_to_any(decode) .and_then(|((kind, kw), args)| split_name_slot(args, kw).ok().map(|(name, rest)| (kind, name, rest))) inline; post-lift the tool binds to ONE primitive.
  • REPL single-form dispatcher — a `:dispatch
    ` command that walks a single form through the registry classifier, reporting the typed kind AND the NAME slot (for "you said `(defmonitor my-monitor …)`, I see `Monitor` named `my-monitor` with 3 spec args"). Pre-lift the REPL would re-derive the same inline composition; post- lift the REPL binds to ONE primitive, sibling shape to how [`Sexp::as_call_to_any`] backs the slice-side dispatcher [`iter_calls_to_any`].

Structural identity binding it to its siblings:

  • as_named_call_to_any(decode) == as_call_to_any(decode).map(|((d, kw), args)| split_name_slot(args, kw).map(|(name, rest)| (d, name, rest)))
  • as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))
  • as_named_call_to_any(decode).is_none() == as_call_to_any(decode).is_none() (the classifier filter face is identical to the bare-kwargs sibling’s)

The returned &str NAME slot and &[Sexp] spec args tail borrow from &self verbatim — no copy, no allocation, same lifetime as Sexp::as_call_to_any’s tail AND crate::compile::split_name_slot’s pair. T is owned because the underlying Sexp::as_call_to_any classifier is FnOnce(&str) -> Option<(T, &'static str)> and T must outlive the helper boundary; consumers projecting to a typed Copy enum (e.g. a closed-set Kind) get the value directly, consumers projecting to a borrowed &'static str (a closed-set head sourced from ClosedSet::ALL.label()) project to &'static str and inherit the static lifetime through the classifier.

Soft face on the classifier filter, strict face on the named gate: “is this a named call whose head decodes through F, and what are its NAME and spec args?” yielding None for “not our head” (skip / fall through, no diagnostic) AND Some(Err(…)) for “our head but malformed NAME” (reject loudly, structural variant). The soft-classifier-then-strict-named composition matches the slice-side iter_named_calls_to_any yielded Result shape (with non-matching forms skipped by the iterator filter) and the expander-surface expand_and_collect_named_calls_to_any collect shape (with Result::collect short-circuiting on the first malformed NAME) — every layer of the cube preserves both faces.

Theory anchor: THEORY.md §VI.1 — generation over composition; the per-form × classifier × named cell of the soft-dispatch cube is a CONSEQUENCE of Sexp::as_call_to_any + crate::compile::split_name_slot, named on the substrate’s Sexp algebra rather than re-derived inline at every per-form named consumer site. THEORY.md §V.1 — knowable platform; the per-form named-classifier projection becomes a NAMED primitive on the Sexp algebra, discoverable by any future authoring tool (LSP, REPL, tatara-check) that holds a single form in isolation. THEORY.md §II.1 invariant 2 — free middle; the slice-side sibling iter_named_calls_to_any now routes through this per-form primitive via the same forms.iter().filter_map(_) skeleton the bare-kwargs row uses to route through Sexp::as_call_to_any, so the bare and named rows share ONE filter-and-fuse implementation skeleton on the Sexp/&[Sexp] algebras.

Frontier inspiration: MLIR’s mlir::dyn_cast<NamedOpInterface>(op) — the typed downcast from a polymorphic IR node onto a NAMED-op interface that exposes both the typed witness AND the symbol-name accessor is the MLIR idiom; as_named_call_to_any is the unstructured-Rust peer on the substrate’s Sexp algebra, with Option<Result<(T, &str, &[Sexp])>> standing in for MLIR’s typed-downcast-then-name-accessor pair, and the Result face carrying the typed structural rejection MLIR encodes via verifier diagnostics. Racket’s syntax-parse ~or* ((~datum defX) name:id arg ...) ((~datum defY) name:id arg ...) on a single syntax object — typed named-form decomposition with name:id capture binding is the Racket idiom; this method is the per-form Rust-typed peer with the typed structural rejection (NamedFormMissingName / NamedFormNonSymbolName) preserved across the boundary.

Source

pub fn as_named_call_to( &self, keyword: &'static str, ) -> Option<Result<(&str, &[Sexp]), LispError>>

Decompose a named call form whose head matches a constant keywordSome(Ok((name, spec_args))) iff this is a non-empty list whose first element is the symbol keyword AND the remaining elements split cleanly into a NAME slot and a spec args tail, Some(Err(…)) iff the head matches but the NAME slot is missing or non-symbol-or-string, None for every shape Sexp::as_call_to rejects.

Constant-keyword sibling of Sexp::as_named_call_to_any and per-form sibling of iter_named_calls_to on the slice algebra. Routes through the typed-decoded sibling with a constant-classifier decoder (|h| (h == keyword).then_some(((), keyword))) — the same constant-classifier composition Sexp::as_call_to uses to route through Sexp::as_call_to_any on the bare-kwargs axis, and that iter_named_calls_to uses to route through iter_named_calls_to_any on the slice algebra. The discarded () typed witness (then_some(((), keyword))) is consumed by the wrapper projection so the consumer’s per-form mapper sees only the (name, spec_args) borrowed pair, matching the bare projection signature on the named axis.

keyword: &'static str threads verbatim through the NamedFormMissingName.keyword / NamedFormNonSymbolName.keyword slots of the named-form gate — same &'static discipline crate::compile::split_name_slot pins at its boundary, AND that iter_named_calls_to pins on the slice algebra. Consumers that want a runtime keyword whose lifetime is shorter use Sexp::as_named_call_to_any directly with a constant-classifier decoder that converts post-resolution.

Structural identity binding it to its siblings:

  • as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))
  • as_named_call_to(k).is_none() == as_call_to(k).is_none()
  • iter_named_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_named_call_to(k))

Theory anchor: see Sexp::as_named_call_to_any — the constant- keyword sibling shares the same lift posture, threading the &'static str keyword constraint through the named-form gate’s canonical-keyword slot rather than admitting an arbitrary runtime keyword.

Source

pub fn as_unquote(&self) -> Option<(UnquoteForm, &Sexp)>

Decompose an unquote-family form into its typed marker and inner expression — Some((UnquoteForm::Unquote, inner)) iff this is ,x (a Sexp::Unquote wrapper), Some((UnquoteForm::Splice, inner)) iff this is ,@x (a Sexp::UnquoteSplice wrapper), None for every other shape (Quote, Quasiquote, Nil, Atom, List).

This is the unquote-family projection — the typed-marker peer of Sexp::as_call for the macro-template substitution surface. Where Sexp::as_call decomposes (op args …) into a (head, args) pair, as_unquote decomposes ,x / ,@x into a (form, inner) pair where form: UnquoteForm is the closed-set typed marker (Unquote for ,, Splice for ,@) and inner: &Sexp is the borrowed body. The pairing of Sexp::Unquote ↔ UnquoteForm::Unquote and Sexp::UnquoteSplice ↔ UnquoteForm::Splice is the structural invariant the macro-expander’s substitution path keys every rejection on — naming the projection lifts the pair from per-callsite discipline (two Sexp::Unquote(inner) arms paired with two UnquoteForm::Unquote literals at distinct sites, two Sexp::UnquoteSplice(inner) arms paired with two UnquoteForm::Splice literals at distinct sites) into ONE typed projection both expansion strategies route through.

Three consumers in macro_expand route through this primitive:

  • compile_node (bytecode-template compile path) — ,x becomes TemplateOp::Subst(idx), ,@x becomes TemplateOp::Splice(idx); both arms share the gate-1+gate-2 composition resolve_unquote_in_params(inner, params, form)? keyed on the typed form projection.
  • substitute top-level (substitute fallback path) — ,x resolves to its bound value, ,@x rejects with LispError::SpliceOutsideList (a splice form with no containing list to flatten into).
  • substitute list-inner (substitute fallback path’s per-item walk) — ,@x items splice their bound list/nil/scalar value into the assembled list builder via [crate::macro_expand::splice_value_into]; non-splice items recurse into substitute.

Pre-lift each site opened the same per-variant match arms — Sexp::Unquote(inner) => … UnquoteForm::Unquote … and Sexp::UnquoteSplice(inner) => … UnquoteForm::Splice … — independently. The (Sexp variant, UnquoteForm variant) pairing was load-bearing across distinct sites yet only enforced by callsite discipline. Post-lift the pair binds at ONE projection function the type system threads through (UnquoteForm, &Sexp): a regression that drifts ONE site’s pairing (e.g. a future emitter that matches Sexp::Unquote(_) but threads UnquoteForm::Splice into unquote_target_symbol — type-checks but renders a misleading diagnostic) becomes structurally impossible.

Soft face, like the rest of the as_* family on Sexp: it answers “is this form an unquote-family marker, and what does it wrap?” and yields None for everything that isn’t (skip / fall through), with no diagnostic. The strict siblings — [crate::macro_expand::splice_value_into] for the bound-list coercion, non_symbol_unquote_target / splice_outside_list for the per-failure-mode rejections — keep their loud-reject posture; this projection is the dispatch face the soft pre-rejection walk binds to.

Structural identity binding it to the unquote-family variants:

  • as_unquote() == Some((UnquoteForm::Unquote, inner)) iff self == Sexp::Unquote(inner)
  • as_unquote() == Some((UnquoteForm::Splice, inner)) iff self == Sexp::UnquoteSplice(inner)
  • as_unquote().is_some() == matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))

The returned &Sexp borrows the inner box’s body verbatim — no clone, no allocation — same lifetime as &self. The closed-set guarantee on UnquoteForm (exactly Unquote ⊎ Splice) is threaded through this projection’s return tuple, so consumers that pattern-match on form: UnquoteForm get rustc-enforced exhaustiveness — a future Sexp variant must extend UnquoteForm AND this match arm together (or stay outside the unquote family and project to None), eliminating the silent two-site extension-drift this lift was already designed to forbid.

Theory anchor: THEORY.md §VI.1 — generation over composition; the (Sexp::Unquote, UnquoteForm::Unquote) and (Sexp::UnquoteSplice, UnquoteForm::Splice) pairings appear ≥3 times across compile_node (2 arms) + substitute (top-level + list-inner) — past the PRIME-DIRECTIVE trigger once the structural shape is named. THEORY.md §V.1 — knowable platform; the unquote-family projection becomes a NAMED primitive on the substrate’s Sexp algebra rather than per-site Sexp::Unquote(_) | Sexp::UnquoteSplice(_) inline matches paired with per-site UnquoteForm::Unquote / UnquoteForm::Splice literals. THEORY.md §II.1 invariant 1 — typed entry; the macro-template substitution surface’s typed-marker projection IS the rust-level typed-entry gate’s structural component, lifted from per-site duplication onto ONE rust method the substrate’s diagnostic promotions hang off of. THEORY.md §II.1 invariant 2 — free middle; both expansion strategies (bytecode compile_node and substitute fallback substitute) route through the SAME projection, so a regression that drifts ONE strategy’s (Sexp variant, UnquoteForm variant) pairing from the other cannot reach the substrate’s runtime — the type system binds both strategies to the projection’s single emission shape.

Frontier inspiration: Racket’s syntax-parse ~or* (~unquote stx) (~unquote-splice stx) pattern — every macro-template pattern over ,id / ,@id binds to ONE typed decomposition that surfaces the marker identity alongside the inner expression; the substrate’s as_unquote is the Rust-typed peer of that pattern, lifted onto the Sexp algebra with UnquoteForm standing in for Racket’s pattern-class identity. MLIR’s typed-IR projection mlir::dyn_cast<UnquoteFamilyOp>(op) — the typed downcast from a polymorphic IR node onto a closed-set op family is the MLIR idiom; as_unquote is the unstructured-projection peer on the substrate’s Sexp algebra, with Option<(UnquoteForm, &Sexp)> standing in for MLIR’s typed downcast result.

Source

pub fn as_unquote_form(&self) -> Option<UnquoteForm>

Soft projection onto the closed-set UnquoteForm template- substitution carving marker — the 2-of-12 carving of the SexpShape algebra covering the two homoiconic template-substitution wrappers (Self::Unquote and Self::UnquoteSplice), which is itself a 2-of-4 subset of the quote-family carving (QuoteForm). Returns Some(UnquoteForm::Unquote) iff this is ,x (a Self::Unquote wrapper), Some(UnquoteForm::Splice) iff this is ,@x (a Self::UnquoteSplice wrapper), None for every other outer shape (Self::Nil, every Self::Atom variant, Self::List, and the two non-substitution quote-family wrappers Self::Quote and Self::Quasiquote).

Direct value-level peer of the shape-level projection SexpShape::as_unquote_form — the pair (Sexp::as_unquote_form, SexpShape::as_unquote_form) binds the (Sexp value, UnquoteForm carving marker) pairing at ONE typed method on each algebra, closing the unquote-subset cell of the (Sexp value → carving marker) matrix. Marker-only sibling of Self::as_unquote (which returns Option<(UnquoteForm, &Sexp)> — marker + wrapped inner) and direct 2-of-4 subset peer of Self::as_quote_form (which covers the 4-of-12 quote-family carving with Option<(QuoteForm, &Sexp)>). Post-lift the substrate’s value-level marker-only carving-marker matrix closes ONE more cell: the atomic axis via Self::as_atom_kind (6-of-12), the residual axis via Self::as_structural_kind (2-of-12), the quote-family axis via Self::as_quote_form().map(|(qf, _)| qf) (4-of-12, marker + inner available via the pre-existing method), and now the unquote- subset axis via Self::as_unquote_form (2-of-12, marker only) — symmetric with the shape-level marker-only projection family on SexpShape.

Composition laws (three-way agreement — bindings): for every s: &Sexp, s.as_unquote_form() == s.as_unquote().map(|(uf, _)| uf) == s.shape().as_unquote_form() == s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form()). Pre-lift the unquote-subset carving marker at the value level was reachable only via one of these three-step compositions — either through the parent Self::as_unquote projection (discarding the inner), through the shape algebra (shape().as_unquote_form()), or through the parent quote-family projection composed with the 2-of-4 subset gate QuoteForm::as_unquote_form. Post-lift the projection lands at ONE typed method on the value algebra, and all three compositions are pinned as agreement laws (see sexp_as_unquote_form_agrees_with_as_unquote_map_marker_for_every_variant, sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant, and sexp_as_unquote_form_agrees_with_as_quote_form_and_quote_form_as_unquote_form_for_every_variant in this module). A regression that drifts any of the four projections from the others surfaces immediately.

Symmetric with Self::as_atom_kind and Self::as_structural_kind on the marker-only shape (returns just the closed-set marker, no inner-payload borrow) — where Self::as_quote_form and Self::as_unquote surface both the marker AND the wrapped inner &Sexp (because the four quote-family arms and the two substitution arms structurally carry a boxed inner value), as_unquote_form returns a marker-only projection: consumers that need the wrapped inner reach the marker-plus-inner sibling Self::as_unquote, while consumers that only need the closed- set carving-marker identity (typed-pattern matchers, diagnostic filters, coverage sweeps, LSP/REPL structural-navigation gates) reach this projection and never allocate the tuple.

Composes cleanly with UnquoteForm::marker to project the value- level substitution carving membership onto its canonical marker string (, / ,@): s.as_unquote_form().map(UnquoteForm::marker) — the marker-string witness for the substitution subset, sibling to s.as_atom_kind().map(AtomKind::label) on the atomic axis, both routing through the closed-set marker enum’s canonical-vocabulary projection at ONE canonical site (UnquoteForm::marker — itself composed through QuoteForm::prefix).

Structural identity (pinned as a truth-table by sexp_as_unquote_form_projects_each_variant_to_canonical_unquote_form and sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes):

  • as_unquote_form() == Some(UnquoteForm::Unquote) iff matches!(self, Sexp::Unquote(_))
  • as_unquote_form() == Some(UnquoteForm::Splice) iff matches!(self, Sexp::UnquoteSplice(_))
  • as_unquote_form() == None iff !matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))

Theory anchor: THEORY.md §V.1 — knowable platform; the substitution- subset carving marker at the value level becomes a NAMED primitive on the substrate’s Sexp algebra rather than a per- site composition through either Self::as_unquote (discarding its &Sexp inner) or Self::shape (walking through the full 12-variant SexpShape closed set to arrive at the 2-of-12 carving marker) or the parent Self::as_quote_form combined with QuoteForm::as_unquote_form (the 2-of-4 subset gate). THEORY.md §II.1 invariant 2 — free middle; every consumer that wants the substitution-subset carving identity without needing the wrapped inner (a future tatara-check predicate (check-value-projects-to-unquote-subset …) that filters diagnostics keyed on the substitution-subset cohort; a future LSP structural-navigation filter that keys on the substitution-subset carving membership at the value level; a future TypedRewriter<TemplateOp> sweep that walks Sexp values whose substitution-arm identity is Some(UnquoteForm::_) regardless of inner payload identity; a future REPL pretty-printer that chooses rendering paths keyed on the value-level substitution carving marker without needing the inner payload) binds to ONE typed method on the value algebra. THEORY.md §VI.1 — generation over composition; the (Sexp variant, UnquoteForm variant) pairing binds at ONE inherent method on the algebra rather than at three parallel compositions (as_unquote().map(…), shape() .as_unquote_form(), as_quote_form().and_then(|(qf, _)| qf.as_unquote_form())), so a regression that drifts ONE composition’s pairing from the others cannot reach the substrate’s runtime — the type system binds all three compositions to the projection’s single emission shape.

Frontier inspiration: MLIR’s mlir::dyn_cast<UnquoteFamilyOp>(op) .map(|op| op.marker()) — every typed rewriter that only needs the op-family identity (without the op’s operands) binds to the typed-downcast projection composed with an operand-discarding marker extract; Sexp::as_unquote_form is the marker-only peer on the substrate’s Sexp algebra, with Option<UnquoteForm> standing in for MLIR’s Optional<OperationName> marker-only downcast result. Racket’s syntax-parse ~or* (~unquote _) (~unquote-splice _) — every syntax-class pattern that keys on the substitution-subset marker identity without binding the inner form; Sexp::as_unquote_form is the Rust-typed peer that surfaces the marker identity through a single primitive on the syntax algebra.

Source

pub fn as_quote_form(&self) -> Option<(QuoteForm, &Sexp)>

Decompose a quote-family form into its typed marker and inner expression — Some((QuoteForm::Quote, inner)) iff this is 'x (a Sexp::Quote wrapper), Some((QuoteForm::Quasiquote, inner)) iff this is `x (a Sexp::Quasiquote wrapper), Some((QuoteForm::Unquote, inner)) iff this is ,x (a Sexp::Unquote wrapper), Some((QuoteForm::UnquoteSplice, inner)) iff this is ,@x (a Sexp::UnquoteSplice wrapper), None for every other shape (Nil, Atom, List).

This is the quote-family projection — the typed-marker peer of Sexp::as_unquote generalized across all four homoiconic prefix-wrappers. Where Sexp::as_unquote keys the macro-template SUBSTITUTION surface on the closed pair {Unquote, Splice} (the two prefixes whose template-time semantic is substitution), as_quote_form keys the WIRE-SHAPE surfaces (Display rendering, Hash discrimination, canonical-form interop) on the closed superset {Quote, Quasiquote, Unquote, UnquoteSplice} — all four prefixes the reader can tokenize and the writer must round-trip. The Sexp::as_unquote projection now derives structurally from as_quote_form’s output via QuoteForm::as_unquote_form — the 2-of-4 subset gate — so the two projections share a SINGLE implementation site on the Sexp algebra and the (Sexp variant, QuoteForm variant) pairing binds at ONE rust function regardless of whether the consumer wants the substitution subset or the wire-shape superset.

Three consumers in this file route through this primitive:

  • Hash for Sexp — the four Quote/Quasiquote/Unquote/ UnquoteSplice arms (pre-lift each carrying its own <discr>.hash(h); inner.hash(h) body) collapse to ONE arm that routes through as_quote_form and reads the discriminator via QuoteForm::hash_discriminator.
  • Display for Sexp — the four write!(f, "<prefix>{inner}") arms (pre-lift each carrying its own literal prefix string) collapse to ONE arm that routes through as_quote_form and reads the prefix via QuoteForm::prefix.
  • Sexp::as_unquote — derives Option<(UnquoteForm, &Sexp)> by composing as_quote_form with QuoteForm::as_unquote_form (the 2-of-4 subset projection), so the macro-template substitution surface inherits the (Sexp variant, marker) pairing through this projection’s typed dispatch rather than re-deriving its own arm-based match.

The closed-set guarantee on QuoteForm (exactly Quote ⊎ Quasiquote ⊎ Unquote ⊎ UnquoteSplice) is threaded through this projection’s return tuple, so consumers that pattern-match on form: QuoteForm get rustc-enforced exhaustiveness — a future Sexp wrapper variant must extend QuoteForm AND this match arm together (or stay outside the quote family and project to None), eliminating the silent multi-site extension-drift this lift was designed to forbid.

Soft face, like the rest of the as_* family on Sexp: it answers “is this form a quote-family marker, and what does it wrap?” and yields None for everything that isn’t (skip / fall through), with no diagnostic.

Structural identity binding it to the quote-family variants and its as_unquote subset sibling:

  • as_quote_form() == Some((QuoteForm::Quote, inner)) iff self == Sexp::Quote(inner)
  • as_quote_form() == Some((QuoteForm::Quasiquote, inner)) iff self == Sexp::Quasiquote(inner)
  • as_quote_form() == Some((QuoteForm::Unquote, inner)) iff self == Sexp::Unquote(inner)
  • as_quote_form() == Some((QuoteForm::UnquoteSplice, inner)) iff self == Sexp::UnquoteSplice(inner)
  • as_quote_form().is_some() == matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))
  • as_unquote() == as_quote_form().and_then(|(qf, inner)| qf.as_unquote_form().map(|uf| (uf, inner)))

The returned &Sexp borrows the inner box’s body verbatim — no clone, no allocation — same lifetime as &self and same posture as Sexp::as_unquote’s tail.

Theory anchor: THEORY.md §VI.1 — generation over composition; the quote-family (Sexp variant, prefix string, hash discriminator) triple appeared inline at three sites (Hash for Sexp, Display for Sexp, as_unquote) — well past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is named. THEORY.md §V.1 — knowable platform; the quote-family typed-marker projection becomes a NAMED primitive on the substrate’s Sexp algebra rather than per-site inline matches paired with per-site discriminator literals and prefix literals. THEORY.md §II.1 invariant 1 — typed entry; the reader’s prefix-to-variant dispatch ([crate::reader::read_quoted]) AND the Display impl’s variant-to-prefix dispatch are dual typed-entry / typed-exit gates over the same closed set; the QuoteForm algebra threads BOTH gates through ONE typed enum so a regression that drifts one side’s prefix from the other (e.g. the reader gains a fifth prefix but the Display impl doesn’t) is no longer a silent two-site divergence — rustc binds both sides to the same closed-set enum. THEORY.md §II.1 invariant 2 — free middle; the three consumers (Hash, Display, as_unquote) route through the SAME projection, so a regression that drifts ONE consumer’s (Sexp variant, marker) pairing from the others cannot reach the substrate’s runtime.

Frontier inspiration: Racket’s syntax-parse ~or* (~quote stx) (~quasiquote stx) (~unquote stx) (~unquote-splice stx) pattern — every macro-template pattern over '/`/,/,@ binds to ONE typed decomposition that surfaces the marker identity alongside the inner expression; the substrate’s as_quote_form is the Rust-typed peer of that pattern, lifted onto the Sexp algebra with QuoteForm standing in for Racket’s pattern-class identity at the homoiconic prefix surface. MLIR’s typed-IR projection mlir::dyn_cast<QuoteFamilyOp>(op) — the typed downcast from a polymorphic IR node onto a closed-set op family is the MLIR idiom; as_quote_form is the unstructured-projection peer on the substrate’s Sexp algebra, with Option<(QuoteForm, &Sexp)> standing in for MLIR’s typed downcast result.

Source

pub fn as_quote_form_marker(&self) -> Option<QuoteForm>

Soft projection onto the closed-set QuoteForm quote-family carving marker — the 4-of-12 carving of the SexpShape algebra covering the four homoiconic prefix-wrappers (Self::Quote, Self::Quasiquote, Self::Unquote, Self::UnquoteSplice). Returns Some(QuoteForm::Quote) iff this is 'x (a Self::Quote wrapper), Some(QuoteForm::Quasiquote) iff this is `x (a Self::Quasiquote wrapper), Some(QuoteForm::Unquote) iff this is ,x (a Self::Unquote wrapper), Some(QuoteForm::UnquoteSplice) iff this is ,@x (a Self::UnquoteSplice wrapper), None for every other outer shape (Self::Nil, every Self::Atom variant, Self::List).

Direct value-level peer of the shape-level projection SexpShape::as_quote_form — the pair (Sexp::as_quote_form_marker, SexpShape::as_quote_form) binds the (Sexp value, QuoteForm carving marker) pairing at ONE typed method on each algebra, closing the quote-family cell of the (Sexp value → carving marker) matrix at the marker-only value-level projection surface. Marker-only sibling of Self::as_quote_form (which returns Option<(QuoteForm, &Sexp)> — marker + wrapped inner). Post-lift the substrate’s value-level marker-only carving-marker matrix closes its FINAL cell: the atomic axis via Self::as_atom_kind (6-of-12), the residual axis via Self::as_structural_kind (2-of-12), the unquote- subset axis via Self::as_unquote_form (2-of-12), and NOW the quote-family axis via Self::as_quote_form_marker (4-of-12) — symmetric with the shape-level marker-only projection family on SexpShape.

Composition laws (two-way agreement — bindings): for every s: &Sexp, s.as_quote_form_marker() == s.as_quote_form().map(|(qf, _)| qf) == s.shape().as_quote_form(). Pre-lift the quote-family carving marker at the value level was reachable only via one of these two-step compositions — either through the parent Self::as_quote_form projection (discarding the wrapped inner via .map(|(qf, _)| qf)) or through the shape algebra (s.shape().as_quote_form(), walking the full 12-variant SexpShape closed set to arrive at the 4-of-12 carving marker). Post-lift the projection lands at ONE typed method on the value algebra, and both compositions are pinned as agreement laws (see sexp_as_quote_form_marker_agrees_with_as_quote_form_map_marker_for_every_variant and sexp_as_quote_form_marker_agrees_with_shape_as_quote_form_for_every_variant in this module).

Superset-gate contract with Self::as_unquote_form: for every s: &Sexp, s.as_unquote_form().is_some() implies s.as_quote_form_marker().is_some() (the 2-of-12 substitution subset is a proper subset of the 4-of-12 quote family). The two non-substitution quote-family wrappers (Self::Quote and Self::Quasiquote) satisfy as_quote_form_marker().is_some() AND as_unquote_form().is_none() — the value-level image of the 2-of-4 subset gate QuoteForm::as_unquote_form. Pinned by sexp_as_quote_form_marker_extends_as_unquote_form_to_full_quote_family.

Structural identity binding it to the quote-family variants:

  • as_quote_form_marker() == Some(QuoteForm::Quote) iff matches!(self, Sexp::Quote(_))
  • as_quote_form_marker() == Some(QuoteForm::Quasiquote) iff matches!(self, Sexp::Quasiquote(_))
  • as_quote_form_marker() == Some(QuoteForm::Unquote) iff matches!(self, Sexp::Unquote(_))
  • as_quote_form_marker() == Some(QuoteForm::UnquoteSplice) iff matches!(self, Sexp::UnquoteSplice(_))
  • as_quote_form_marker() == None iff !matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))

Theory anchor: THEORY.md §V.1 — knowable platform; the quote- family carving marker at the value level becomes a NAMED primitive on the substrate’s Sexp algebra rather than a per- site two-step composition through either Self::as_quote_form (discarding its &Sexp inner) or Self::shape (walking through the full 12-variant SexpShape closed set to arrive at the 4-of-12 carving marker). THEORY.md §II.1 invariant 2 — free middle; every consumer that wants the quote- family carving identity without needing the wrapped inner (a future tatara-check predicate (check-value-projects-to-quote- family …) that filters diagnostics keyed on the quote-family cohort; a future LSP structural-navigation filter that keys on the quote-family carving membership at the value level; a future TypedRewriter<QuoteFamilyOp> sweep that walks Sexp values whose quote-family arm identity is Some(QuoteForm::_) regardless of inner payload identity; a future REPL pretty- printer that chooses rendering paths keyed on the value-level quote-family carving marker without needing the inner payload) routes through ONE typed method rather than reaching into one of the two composition sites, and both compositions are pinned as agreement laws so a regression that drifts ONE composition’s pairing from the other cannot reach the substrate’s runtime. THEORY.md §VI.1 — generation over composition; the (Sexp variant, QuoteForm variant) pairing binds at ONE inherent method on the algebra rather than at two parallel compositions, so a future extension (e.g. a fifth Sexp quote-family wrapper) lands at ONE match arm in the parent as_quote_form projection and inherits through this method’s structural composition.

Frontier inspiration: MLIR’s mlir::dyn_cast<QuoteFamilyOp>(op) .map(|op| op.marker()) — every typed rewriter that only needs the op-family identity (without the op’s operands) binds to the typed-downcast projection composed with an operand-discarding marker extract; Sexp::as_quote_form_marker is the marker-only peer on the substrate’s Sexp algebra, with Option<QuoteForm> standing in for MLIR’s Optional<OperationName> marker-only downcast result. Racket’s syntax-parse ~or* (~quote _) (~quasiquote _) (~unquote _) (~unquote-splice _) — every syntax-class pattern that keys on the quote-family marker identity without binding the inner form; Sexp::as_quote_form_marker is the Rust-typed peer that surfaces the marker identity through a single primitive on the syntax algebra.

Source

pub fn expect_quote_form(&self) -> (QuoteForm, &Sexp)

Quote-family projection, asserted-total face of Sexp::as_quote_form. Returns (QuoteForm, &Sexp) verbatim — same borrowed-inner posture, same closed-set marker — but panics with QUOTE_FAMILY_PROJECTION_INVARIANT instead of yielding None for non-quote-family variants. Use AFTER an outer pattern match has narrowed the discriminant union to the quote family (Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_)); the panic message states the invariant the caller’s outer pattern already proves.

Pre-lift the five production-site quote-family-arm consumers — Hash for Sexp::hash_discriminator, Display for Sexp::prefix, domain::sexp_shape, domain::sexp_to_json, interop::iac_forge_tag — each carried a verbatim copy of the 4-arm wildcard pattern AND a verbatim copy of the inline .as_quote_form().expect("matched quote-family variant must project to Some via as_quote_form") re-projection. The (pattern, expect message) pair appeared bit-for-bit at FIVE sites. Post-lift the expect message lives at ONE named const and the projection-with- assertion lives at ONE primitive on the Sexp algebra; the five callsites collapse to ONE typed query each. A future quote-family extension that drifts ONE site’s panic text from the others becomes structurally impossible (one const, one method); a future site that needs the same “outer-narrowed, total projection” shape lands on this primitive directly without re-deriving the expect literal.

#[track_caller] ensures a panic surfaces the consumer’s source position, not this projection’s — so the diagnostic stays load-bearing under the lift.

Sibling posture to the expect_* family of typed-projection asserted-total faces across the substrate’s closed-set algebras (Option::expect, Result::expect) — the assertion is the same shape, the message is named on the algebra it asserts about.

§Panics

Panics with QUOTE_FAMILY_PROJECTION_INVARIANT if self is not a quote-family variant. The outer pattern match at every caller site is the proof of the invariant; the panic is the static fall-through for a regression that drifts that proof.

Theory anchor: THEORY.md §VI.1 — generation over composition; the (4-arm wildcard pattern, expect re-projection) pair appeared bit- for-bit at five production sites — well past the ≥2 PRIME-DIRECTIVE trigger. THEORY.md §V.1 — knowable platform; the panic message and the projection-with-assertion are now ONE named primitive on the substrate’s Sexp algebra, structurally binding the invariant across every consumer that asserts an outer narrowing.

Source

pub fn iac_forge_tag(&self) -> Option<&'static str>

Cross-crate canonical iac-forge tag for the outer Sexp value — the OUTER-VALUE peer of the shape-level [crate::error::SexpShape ::iac_forge_tag] one algebra layer down. Some(&'static str) for the four homoiconic prefix-wrapper arms — Self::Quote → Some("quote"), Self::Quasiquote → Some("quasiquote"), Self::Unquote → Some("unquote"), Self::UnquoteSplice → Some("unquote-splicing") — and None for the outer atomic-payload arm (Self::Atom) AND the two structural-residual arms (Self::Nil, Self::List). The 4-of-7 partial projection on the outer-Sexp algebra surfaces crate::ast::QuoteForm::iac_forge_tag’s cross-crate canonical- form tag surface at the outermost value-carrier algebra level, composed through the pre-existing Self::shape projection and crate::error::SexpShape::iac_forge_tag’s shape-level partial projection.

Composition law: sexp.iac_forge_tag() == sexp.shape().iac_forge_tag() for every sexp: &Sexp — the outer- Sexp cross-crate canonical-form tag surface routes through Self::shape into the shape-level partial projection, which in turn composes through crate::error::SexpShape::as_quote_form with crate::ast::QuoteForm::iac_forge_tag’s canonical 4-of-4 closed-set tag projection. Post-lift the outer-Sexp cross-crate canonical-form tag surface closes at FOUR typed layers: outer Self::iac_forge_tag (7-arm outer dispatch on the outer Sexp algebra, this method) → shape-level crate::error::SexpShape::iac_forge_tag (12-arm shape-level dispatch on the crate::error::SexpShape algebra) → quote-family carving crate::error::SexpShape::as_quote_form (4-of-12 quote-family sub-carving) → sub-carving tag crate::ast::QuoteForm::iac_forge_tag (4-arm quote-family sub-carving’s canonical-form tag projection).

Pre-lift a consumer with a typed Sexp value in hand (a generation-side canonical-form emitter, a downstream iac-forge attestation site, an LSP / REPL / audit-trail metric keyed on the observed outer value) wanting the cross-crate iac-forge canonical tag string had to spell the two-step composition sexp.shape().iac_forge_tag() at every callsite, or route through Self::as_quote_form_marker composed with crate::ast::QuoteForm::iac_forge_tag via map as the crate::interop (removed) From<&Sexp> for iac_forge::SExpr impl does for its four quote-family arms via Self::expect_quote_form composed with crate::ast::QuoteForm::iac_forge_tag. Post-lift the outer-Sexp canonical-form tag projection binds at ONE typed-algebra method on the outer value-carrier — the SEVENTH consumer of the outer-Sexp projection surface (sibling of Self::shape, Self::type_name, Self::hash_discriminator, Self::as_atom, Self::as_list, Self::as_quote_form, Self::as_quote_form_marker, Self::as_unquote, Self::as_unquote_form), matching the same shape-composition posture Self::hash_discriminator takes through the outer → shape one-step delegation.

The Option<&'static str> return shape mirrors crate::error::SexpShape::iac_forge_tag’s partial-projection shape one algebra level down — the outer-Sexp seven-arm closed set’s projection PARTIALIZES on the three non-quote-family shapes (Nil, Atom, List) exactly as the shape-level twelve-arm closed set’s projection PARTIALIZES on the eight non-quote-family shapes. The kernel’s outer cardinality (three: Nil / Atom / List) matches the shape-level kernel’s cardinality (eight) through Self::shape’s six-atomic-arms → outer Atom collapse — the outer three-arm kernel {Nil, Atom, List} corresponds to the shape-level eight-arm kernel {Nil, Symbol, Keyword, String, Int, Float, Bool, List} under the outer → shape projection.

The &'static str lifetime is load-bearing: every iac-forge consumer projects through this method into the canonical 2-element-list head without an allocation, parallel to how crate::ast::QuoteForm::iac_forge_tag on the sub-carving, crate::error::SexpShape::iac_forge_tag on the shape-level projection, and crate::error::UnquoteForm::iac_forge_tag on the template-substitution subset project their respective closed sets. A future eighth Sexp variant (e.g. a hypothetical Vector for #(...) reader syntax, Map for {...}, Char for #\x) extends crate::error::SexpShape (adding a None-arm non-quote-family shape) — this method picks up the new arm’s None mechanically through the shape composition, with rustc’s exhaustiveness binding the extension end-to-end at crate::error::SexpShape::as_quote_form’s closed match.

Theory anchor: THEORY.md §V.1 — knowable platform; the (outer Sexp variant, canonical iac-forge tag) pairing becomes a TYPE projection on the outermost value-carrier algebra rather than an inline .shape().iac_forge_tag() two-step at every consumer. A typo or swap at the projection site is no longer a runtime tag drift but a compile error against the typed composition — the SexpSexpShapeQuoteForm ↔ tag string chain is rustc- enforced end-to-end. THEORY.md §II.1 invariant 2 — free middle; the (outer value, canonical iac-forge tag) pairing now binds at ONE site on the outer-Sexp algebra, composing through the pre- existing shape-level partial projection rather than duplicating the four-arm match here. THEORY.md §VI.1 — generation over composition; the outer-Sexp cross-crate canonical-form tag surface closes at FOUR typed layers (outer → shape → carving → sub-carving-tag), each keyed on the SAME canonical-form tag projection carried at the closed-set sub-carving level.

Frontier inspiration: MLIR’s mlir::Operation::getName() typed projection composed with mlir::OperationName::getStringRef() — narrowing an operation-carrier value through its typed op-name identity yields the canonical cross-boundary string identity in ONE typed composition. Self::iac_forge_tag is the Rust-typed peer where the “project to shape” step (Self::shape) composes with the “read the shape’s canonical tag” step (crate::error::SexpShape::iac_forge_tag) into ONE outer-value projection.

Source

pub fn prefix(&self) -> Option<&'static str>

Canonical reader-punctuation prefix for the outer Sexp value — the OUTER-VALUE peer of the shape-level crate::error::SexpShape::prefix one algebra layer down. Some(&'static str) for the four homoiconic prefix-wrapper arms — Self::Quote → Some("'"), Self::Quasiquote → Some("“), Self::Unquote → Some(”,“), Self::UnquoteSplice → Some(”,@“)— andNone for the outer atomic-payload arm ([Self::Atom]) AND the two structural-residual arms ([Self::Nil], [Self::List]). The 4-of-7 partial projection on the outer-Sexp algebra surfaces [crate::ast::QuoteForm::prefix]'s reader-punctuation surface at the outermost value-carrier algebra level, composed through the pre-existing [Self::shape] projection and [crate::error::SexpShape::prefix`]’s shape-level partial projection.

Composition law: sexp.prefix() == sexp.shape().prefix() for every sexp: &Sexp — the outer-Sexp reader-punctuation surface routes through Self::shape into the shape-level partial projection, which in turn composes through crate::error::SexpShape::as_quote_form with crate::ast::QuoteForm::prefix’s canonical 4-of-4 closed-set prefix projection. Post-lift the outer-Sexp reader-punctuation surface closes at FOUR typed layers: outer Self::prefix (7-arm outer dispatch on the outer Sexp algebra, this method) → shape-level crate::error::SexpShape::prefix (12-arm shape- level dispatch on the crate::error::SexpShape algebra) → quote-family carving crate::error::SexpShape::as_quote_form (4-of-12 quote-family sub-carving) → sub-carving prefix crate::ast::QuoteForm::prefix (4-arm quote-family sub- carving’s canonical reader-punctuation projection).

Pre-lift a consumer with a typed Sexp value in hand (an [fmt::Display for Sexp] impl that renders the four quote-family arms as <prefix><inner>, an LSP hover / REPL completion that echoes the source-punctuation prefix of a wrapper value, an audit-trail metric keyed on the observed outer value) wanting the canonical reader-punctuation prefix string had to spell the two-step composition sexp.shape().prefix() at every callsite, or route through Self::as_quote_form_marker composed with crate::ast::QuoteForm::prefix via map as the [fmt::Display for Sexp] impl does for its four quote-family arms via Self::expect_quote_form composed with crate::ast::QuoteForm::prefix. Post-lift the outer-Sexp reader-punctuation projection binds at ONE typed-algebra method on the outer value-carrier — the natural next rung on the trajectory mirroring the Self::iac_forge_tagcrate::error::SexpShape::iac_forge_tag ladder one vocabulary axis over, matching the same shape-composition posture Self::hash_discriminator and Self::iac_forge_tag take through the outer → shape one-step delegation.

The Option<&'static str> return shape mirrors crate::error::SexpShape::prefix’s partial-projection shape one algebra level down — the outer-Sexp seven-arm closed set’s projection PARTIALIZES on the three non-quote-family shapes (Nil, Atom, List) exactly as the shape-level twelve-arm closed set’s projection PARTIALIZES on the eight non-quote-family shapes. The kernel’s outer cardinality (three: Nil / Atom / List) matches the shape-level kernel’s cardinality (eight) through Self::shape’s six-atomic-arms → outer Atom collapse — the outer three-arm kernel {Nil, Atom, List} corresponds to the shape-level eight-arm kernel {Nil, Symbol, Keyword, String, Int, Float, Bool, List} under the outer → shape projection.

The reader-punctuation vocabulary this method projects ("'" / "`" / "," / ",@") is INTENTIONALLY DISJOINT from the two sibling &'static str outer-value projection axes:

  • Self::iac_forge_tag — cross-crate canonical form ("quote" / "quasiquote" / "unquote" / "unquote-splicing"), BLAKE3 attestation keys, render-cache shape (load-bearing for byte-identical inter-crate compatibility with the iac-forge ecosystem);
  • Self::type_name — operator-facing diagnostic label ("nil" / "atom" / "list" / "quote" / "quasiquote" / "unquote" / "unquote-splice") on the outer 7-arm surface, crate::error::LispError::TypeMismatch’s got rendering, REPL / LSP shape-of-witness surface.

This method projects the reader’s SOURCE-TEXT vocabulary — the four punctuation characters that appear literally in Lisp source at each variant’s homoiconic prefix. The three outer-value closed-set projections key the SAME seven-arm outer algebra on THREE distinct &'static str vocabularies (source-punctuation, diagnostic-label, cross-crate canonical-form); consolidating any two would silently break either the reader round-trip, the operator-facing diagnostic surface, OR the iac-forge attestation pipeline. The three vocabularies’ distinctness is pinned bit-for- bit through the composition law across the closed-set typed algebra.

The &'static str lifetime is load-bearing: every reader / LSP / REPL / [fmt::Display for Sexp] consumer projects through this method into the canonical prefix character without an allocation, parallel to how crate::ast::QuoteForm::prefix on the sub- carving, crate::error::SexpShape::prefix on the shape-level projection, Self::iac_forge_tag on the cross-crate canonical- form axis, and crate::error::UnquoteForm::marker on the template-marker axis project their respective closed sets. A future eighth Sexp variant (e.g. a hypothetical Vector for #(...) reader syntax, Map for {...}, Char for #\x) extends crate::error::SexpShape (adding a None-arm non- quote-family shape) — this method picks up the new arm’s None mechanically through the shape composition, with rustc’s exhaustiveness binding the extension end-to-end at crate::error::SexpShape::as_quote_form’s closed match.

Theory anchor: THEORY.md §V.1 — knowable platform; the (outer Sexp variant, reader-punctuation prefix) pairing becomes a TYPE projection on the outermost value-carrier algebra rather than an inline .shape().prefix() two-step at every consumer. A typo or swap at the projection site is no longer a runtime prefix drift but a compile error against the typed composition — the SexpSexpShapeQuoteForm ↔ prefix character chain is rustc-enforced end-to-end. THEORY.md §II.1 invariant 2 — free middle; the (outer value, reader-punctuation prefix) pairing now binds at ONE site on the outer-Sexp algebra, composing through the pre-existing shape-level partial projection rather than duplicating the four-arm match here. THEORY.md §VI.1 — generation over composition; the outer-Sexp reader-punctuation surface closes at FOUR typed layers (outer → shape → carving → sub-carving-prefix), each keyed on the SAME reader-punctuation projection carried at the closed-set sub- carving level.

Frontier inspiration: MLIR’s mlir::Operation::getName() typed projection composed with mlir::OperationName::getStringRef() — narrowing an operation-carrier value through its typed op-name identity yields the canonical cross-boundary string identity in ONE typed composition. Self::prefix is the Rust-typed peer where the “project to shape” step (Self::shape) composes with the “read the shape’s canonical reader-punctuation” step (crate::error::SexpShape::prefix) into ONE outer-value projection — sibling of Self::iac_forge_tag one vocabulary axis over on the cross-crate canonical-form surface.

Source

pub fn node_count(&self) -> usize

Total structural node count of the outer Sexp value — one node per outer-algebra arm plus the recursive node count of each child. Self::Nil and Self::Atom contribute one node apiece (the outer arm itself); Self::List contributes one node for the outer arm plus the summed node count of each child element; the four homoiconic wrapper arms (Self::Quote, Self::Quasiquote, Self::Unquote, Self::UnquoteSplice) each contribute one node for the outer arm plus the node count of the wrapped inner form. The projection is a structural size on the AST — every closed-set arm counts as one, so the count is well-defined on ANY Sexp value regardless of how it was constructed.

Load-bearing arithmetic identities:

  • Sexp::Nil.node_count() == 1
  • Sexp::Atom(_).node_count() == 1
  • Sexp::list(items).node_count() == 1 + sum(item.node_count())
  • Sexp::quote(inner).node_count() == 1 + inner.node_count()
  • (peer identity for each other quote-family arm)

The identities compose: node_count is monotone in tree growth (a strictly-larger tree — one containing more arms — has a strictly-larger count), so a resource ceiling keyed on node_count bounds the total AST arm-count reachable at that ceiling. A future Self::UnquoteSplice wrapper appearing inside a list contributes one node for the wrapper AND one node for the outer list arm plus the node count of the wrapper’s inner form — the identity holds compositionally through the wrapper’s Box<Sexp> payload.

Consumers so far: crate::macro_expand::Expander’s max_expansion_size ceiling — the RESOURCE-axis peer of the max_expansion_depth (recursion length) and max_cache_entries (memoization width) ceilings — projects the freshly-applied macro expansion through node_count to decide whether the result crosses the “expansion bomb” threshold on the OUTPUT-SIZE axis. A #[derive(TataraDomain)] consumer that wants to reject “this macro produced a giant blob” at the expander boundary now inherits the projection mechanically through the ceiling; no per-consumer walker discipline required.

The usize return shape is the natural resource-count carrier — sibling to HashMap::len (the cache-width ceiling consumes) and to the usize depth counter (the recursion ceiling consumes) — so all three ceilings compose against a single arithmetic type without cross-cast overhead.

Theory anchor: THEORY.md §V.1 — knowable platform; the structural size of a value on the outer Sexp algebra becomes a first-class TYPED projection rather than a per-consumer hand-rolled walker. THEORY.md §VI.1 — generation over composition; a future ceiling on a subtree count (e.g. “reject any expansion whose LIST arms exceed N”) emerges naturally as a peer projection on the same closed-set algebra, not as a separate walker.

Trait Implementations§

Source§

impl Clone for Sexp

Source§

fn clone(&self) -> Sexp

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Sexp

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Sexp

Source§

fn deserialize<__D>( __deserializer: __D, ) -> Result<Sexp, <__D as Deserializer<'de>>::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for Sexp

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for Sexp

Source§

fn hash<H>(&self, h: &mut H)
where H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl MacroArgCarrier for Sexp

Source§

type Site = ()

Sexp carries no position, so there is nothing to thread — the unit site is the structural statement that this carrier synthesizes values without context.

Source§

fn lift_default(default: &Sexp, _: ()) -> Sexp

Lift a template-authored default form into this carrier, for an &optional slot the call left unfilled.
Source§

fn collect_rest(items: Vec<Sexp>, _: ()) -> Sexp

Collect the &rest surplus into one carrier value.
Source§

impl PartialEq for Sexp

Source§

fn eq(&self, other: &Sexp) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Sexp

Source§

fn serialize<__S>( &self, __serializer: __S, ) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Sexp

Auto Trait Implementations§

§

impl Freeze for Sexp

§

impl RefUnwindSafe for Sexp

§

impl Send for Sexp

§

impl Sync for Sexp

§

impl Unpin for Sexp

§

impl UnsafeUnpin for Sexp

§

impl UnwindSafe for Sexp

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.