What happens: The sparse-decoration lint flags any abstract formation with exactly one attribute (@) as redundant wrapping. But when the @ expression contains a ^ reference, the wrapper is not redundant — it's what creates the ρ binding boundary ^ resolves through. Inlining the expression into the parent shifts what ^ points to (one level up), changing behavior rather than just removing boilerplate.
Example (bool.eo in eo-runtime):
[x] > a
take > @
[] > take ← flagged: "Sparse decoration is prohibited"
^.x > @
take exists so that, when reached via (a 42).take, its ^ resolves to that specific a-instance. Removing the wrapper and writing ^.x > @ directly inside a makes ^ resolve to whatever contains a instead — not the same object, not the same value.
Other occurrences with the same shape in eo-runtime:
bool.eo:115 — [] > a; ^.return > @ (plus. 42)
dataized.eo:57 — [] > func; ^.m.put (^.m.as-number.plus 1) > @
malloc.eo:418 — [] > a; seq > @ * (^.m.put(...), 42)
What happens: The
sparse-decorationlint flags any abstract formation with exactly one attribute (@) as redundant wrapping. But when the@expression contains a^reference, the wrapper is not redundant — it's what creates theρbinding boundary^resolves through. Inlining the expression into the parent shifts what^points to (one level up), changing behavior rather than just removing boilerplate.Example (
bool.eoineo-runtime):takeexists so that, when reached via(a 42).take, its^resolves to that specifica-instance. Removing the wrapper and writing^.x > @directly insideamakes^resolve to whatever containsainstead — not the same object, not the same value.Other occurrences with the same shape in
eo-runtime:bool.eo:115—[] > a; ^.return > @ (plus. 42)dataized.eo:57—[] > func; ^.m.put (^.m.as-number.plus 1) > @malloc.eo:418—[] > a; seq > @ * (^.m.put(...), 42)