Skip to content

Commit 9eff213

Browse files
committed
docs(AI.a2ml): add 4 idiomatic patterns + 3 directives from idaptik feedback
Closes #45 #46 #47 #48 #49 #50 #52. Adds patterns surfaced during the IDApTIK Wave 3 pilot exercise: * per-element-error-extraction (closes #46) — polymorphic-per-element helper for accumulating errors from heterogeneously-typed Validation values; eliminates the dominant ReScript→AffineScript migration antipattern documented in idaptik's ProvenBridge.idr (4 sites). * fuel-based-total-recursion (closes #47) — canonical pattern for provably-total recursive parsers/interpreters. Counter-pattern to depth-tracking, which the totality checker cannot see as well-founded (depth increments rather than decreases). * validation-chain-over-tuple-match (closes #49) — Validation applicative chain replaces N-tuple `case` matches. N>=5 tuple matches blow ambiguity-resolution budgets in real translation (verified in idaptik's parseLevelJson). * status-sum-vs-string-enum (closes #52) — sum types replace stringly-typed dispatch. Small but pervasive. Adds three new directives: * stdlib-floor (closes #45) — Validation/Result/Option/Either are stdlib floor; reject re-implementations. * eta-style (closes #48) — eta-expand definitions when a function has named pi-binders. Point-free is a parser-cascade trigger. * no-side-effect-imports (closes #50) — `let _ = X.foo` for module-load side effects must rewrite to explicit registration in AffineScript (no module-load-time side effect in wasm). All seven additions cross-reference idaptik's commit hyperpolymath/idaptik@98f110ce and the LESSONS.md catalogue at idaptik/migration/main/LESSONS.md.
1 parent 2f43839 commit 9eff213

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

  • docs/guides/frontier-programming-practices

docs/guides/frontier-programming-practices/AI.a2ml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,34 @@
138138
(composition "phantom + effect rows")
139139
(purpose "Mark values or functions as pure or impure in the type system for optimisation or testing.")
140140
(sketch "fn pure_compute(x: Int): Int // no effect row = pure\nfn io_op(): String / {IO} // effect row declares IO"))
141+
142+
(pattern
143+
(name "per-element-error-extraction")
144+
(composition "polymorphic-per-element + Validation")
145+
(purpose "Accumulate errors from heterogeneously-typed Validation values without forcing them into a uniform list.")
146+
(anti-pattern "List (Validation E a) when each element has a different `a` — non-starter type-theoretically; cannot unify a single `a` across heterogeneous components.")
147+
(sketch "let allErrs = errsOf v1 ++ errsOf v2 ++ errsOf v3\n where errsOf : Validation E a -> List E"))
148+
149+
(pattern
150+
(name "fuel-based-total-recursion")
151+
(composition "Nat parameter + structural decrease")
152+
(purpose "Make recursive parsers / interpreters / search algorithms provably total without sized types.")
153+
(anti-pattern "Depth tracking with `if depth > N then bail` — runtime guarantee, not provable. Totality checker cannot see incrementing parameters as well-founded.")
154+
(sketch "fn parse(fuel: Nat, input: List Char) { match fuel { Z => Err(\"depth\"); S(f) => recurse(f, ...) } }"))
155+
156+
(pattern
157+
(name "validation-chain-over-tuple-match")
158+
(composition "Validation applicative + <*>")
159+
(purpose "Accumulate errors across N independent extractions without N-tuple matches.")
160+
(anti-pattern "case (a, b, c, d, e, f, g) of (Ok, Ok, Ok, ...) => ... — N>=5 tuple matches blow type inference / ambiguity-resolution budgets.")
161+
(sketch "MkRecord <$> a <*> b <*> c <*> d <*> e <*> f <*> g"))
162+
163+
(pattern
164+
(name "status-sum-vs-string-enum")
165+
(composition "sum types")
166+
(purpose "Distinguish status / kind values at compile time. Replaces stringly-typed dispatch (\"compromised\" / \"clean\" / etc.).")
167+
(anti-pattern "dispatch(deviceId, \"compromised\") — typo \"compromied\" silently mis-matches at runtime.")
168+
(sketch "type DeviceStatus = Compromised | Clean | Suspect | Unknown\nfn dispatch(d: DeviceId, s: DeviceStatus)"))
141169
)
142170

143171
(agent-behavior
@@ -157,6 +185,12 @@
157185
(rule "Work that belongs in the Typed WASM project goes to the Typed WASM project. Never add it to AffineScript as a shortcut."))
158186
(honesty-in-documentation
159187
(rule "STATE.a2ml, README, and related docs must reflect what the code actually enforces, not what it aspires to. A 'dune build passing' is not a behavioural test and does not count as 'feature complete'."))
188+
(stdlib-floor
189+
(rule "Validation[E,A], Result[E,A], Option[A], and Either[E,A] are stdlib floor. Reject any project that re-implements them. Pointing migrators at canonical types prevents the heterogeneous-list-as-homogeneous antipattern."))
190+
(eta-style
191+
(rule "When a function has named pi-binders ((name : Type) -> ...), prefer eta-expanded definitions over point-free. Point-free is a parser-cascade trigger when the eta-rewrite's argument name disagrees with the declared signature's name."))
192+
(no-side-effect-imports
193+
(rule "AffineScript modules do not run code at load time. Migrating ReScript/JS code that uses `let _ = X.foo` for module-load side effects must be rewritten to call an explicit registration function. There is no implicit module-load-time side effect to hook in wasm."))
160194
)
161195

162196
(relationship-with-typed-wasm-project

0 commit comments

Comments
 (0)