# The takeaway Consumed data must block recursion. Control data must not drive recursion. Branches with work must be lazy. Top-level fixed points must be hidden behind wrappers. Fixed-format data should be destructured finitely, not sliced recursively. ## Rules for normalization-safe `tricu` A top-level definition must normalize when its runtime inputs are still abstract. Therefore, avoid any shape where known control data can unfold recursion before the consumed data is available. ## 1. Put consumed data first Recursive workers should take the structure they consume before counters, indexes, limits, accumulators, or other control state. Avoid: ```tricu worker index records state ``` Prefer: ```tricu worker records index state ``` The worker’s first real operation should usually be a case split on the consumed value: ```tricu worker_ = (self records state : lazyList nilCase consCase records) ``` ## 2. Do not use generic recursive consumers on abstract fixed-format data Avoid applying helpers like these to abstract values in top-level-normalized definitions: ```tricu take n xs drop n xs nth n xs length xs startsWith? prefix xs bytesTake n bytes bytesDrop n bytes ``` These can be driven by known counters, indexes, lengths, or prefixes while `xs` is still abstract. For fixed-format data, use finite destructuring helpers instead: ```tricu withNodePayloadForkIndices payload shortK indicesK hashShard hash ``` This keeps the recursion bounded by syntax, not by a runtime counter. ## 3. Use lazy eliminators when a branch contains work If a branch contains recursion, IO construction, parsing, lookup, response construction, or anything that may recurse internally, do not pass it as an ordinary branch value. Avoid: ```tricu matchBool resultNow (self rest state) cond ``` Prefer: ```tricu lazyBool (_ : resultNow) (_ : self rest state) cond ``` Same rule for result, maybe, and list elimination: ```tricu lazyBool lazyResult lazyMaybe lazyList ``` Strict eliminators are safe only when both branches are already cheap normal forms. ## 4. Do not expose top-level fixed points directly Avoid top-level definitions like: ```tricu foo_ = y (self input state : ...) ``` Prefer the library-style split: ```tricu foo_ = (self input state : ...) foo = (input state : y foo_ input state) ``` This prevents each independently-normalized top-level definition from trying to normalize the fixed point itself. ## 5. Keep recursive self-application small and structurally progressing Prefer recursive calls shaped like: ```tricu self rest nextState ``` over wide calls like: ```tricu self rest index i limit acc flags ``` Pack non-consumed state into a record/pair if needed. The consumed argument should visibly progress: ```tricu self rest nextState ``` not restart from the original structure: ```tricu self originalRecords newIndex newState ``` Restarting from the original input inside recursive branches can create residual trees with no obvious structural progress. ## 6. Recursive state updates must be non-recursive Do not call a recursive helper while constructing the next recursive state. Avoid: ```tricu self rest (listSnoc acc value) ``` because `listSnoc` is itself recursive. Prefer constant-time constructors: ```tricu self rest (pair value acc) ``` If order matters, reverse later only when the input is concrete, or store explicit indexes in an association list. ## 7. Do not rebuild from the whole input when a prefix invariant exists If validation guarantees child references point backward, use that invariant. Avoid: ```tricu buildTree allRecords childIndex ``` inside the build of each node. Prefer: ```tricu lookup childIndex builtPrefix ``` For Arboricx nodes, this meant scanning records once left-to-right and resolving children from `builtTrees`. ## 8. Make route/path helpers consumed-data-driven For request paths, hashes, and byte strings, avoid counter/prefix-driven recursive operations over abstract request data. Avoid: ```tricu take 3 hash drop 23 target startsWith? prefix target ``` Prefer: ```tricu hashShard hash stripPrefix prefix target ``` where the helper case-analyzes the consumed runtime data before recurring. For fixed small slices like the first three hash bytes, use finite destructuring rather than `take`. ## 9. Treat top-level normalization as stricter than runtime evaluation A function can be semantically correct at runtime and still fail import normalization. Ask this for every top-level definition: ```text Can this normalize while all of its arguments are unknown? ``` If the answer depends on “the branch will not be taken” or “the input will be concrete by then,” the definition is probably not normalization-safe. ## 10. When a definition hangs alphabetically, inspect reachable dependencies The alphabetically first hanging definition is not necessarily the root cause. It may simply be the first definition that reaches a later problematic helper. Debug by replacing reachable branches with constants: ```tricu foo = (... : pure notFoundResponse) ``` Then add back one dependency at a time. If a constant version normalizes, the issue is in reachable branch work, not the wrapper itself. ## Compact checklist Before adding or exporting a definition, check: ```text 1. Does every recursive worker consume unknown data first? 2. Is every recursive branch thunked with lazy eliminators? 3. Is `y` applied inside the public wrapper, not exposed as a top-level worker value? 4. Are recursive self-calls visibly progressing on consumed data? 5. Are recursive state updates constant-time? 6. Are `take`, `drop`, `nth`, `length`, `startsWith?`, or byte slicing used on abstract data? 7. Could a known counter, index, prefix, or length drive recursion? 8. Are fixed-format fields parsed with finite destructuring helpers? 9. Does any branch construct dynamic paths/responses from abstract data using recursive list helpers? 10. Can the definition normalize with all runtime arguments still unknown? ```