Data-first recursive consumers in readBytes

Reorder recursive byte-stream consumers so the consumed input is inspected
before loop-control arguments can drive evaluation. Previously, partially
applying `readBytes` to a known count, such as `readBytes 2`, allowed the
evaluator to specialize the recursive worker using known counter values
while the byte stream was still abstract. This caused symbolic recursion
over unknown input and produced an enormous normal form.

The recursive worker now takes the byte stream first and immediately
case-analyzes it. As a result, partial application blocks at the input
boundary instead of unrolling the counter loop.

This preserves the fully-applied behavior of `readBytes`, while making partial
application such as `readBytes 2` normalize safely.
This commit is contained in:
2026-05-07 10:07:43 -05:00
parent 37d57044e2
commit e8ab61dbaa
5 changed files with 319 additions and 47 deletions

View File

@@ -9,11 +9,14 @@ readArborixMagic = (bs : expectBytes arborixMagic bs)
readArborixHeader = (bs :
bindResult (readArborixMagic bs)
(_ r0 :
bindResult (readU16BEBytes r0)
(major r1 :
bindResult (readU16BEBytes r1)
(minor r2 :
bindResult (readU32BEBytes r2)
(sections r3 :
ok (pair major (pair minor sections)) r3)))))
(_ afterMagic :
bindResult (readBytes 2 afterMagic)
(majorVersion afterMajor :
bindResult (readBytes 2 afterMajor)
(minorVersion afterMinor :
bindResult (readBytes 4 afterMinor)
(sectionCount afterSectionCount :
ok
(pair majorVersion
(pair minorVersion sectionCount))
afterSectionCount)))))