tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

README.md (7984B)


Handler library validation fixtures

library.tri is the normal native-library corpus, run by test/handler_library.py. Its direct benchmark intentionally uses the lighter production server route. The library consumer has more validation obligations.

The corpus also checks H.checkWith and H.checkRequest with transformed values, nonempty success/rejection remainders, malformed truth flags, and divergent unselected callbacks/payloads. Scripted requests check that rejected inputs stop before dispatch and reply checks run after the observed request. Logger rejection checks preserve labels and leave the sink uncalled.

equivalent.tri is compiled only by test/handler_costs.py, through a generated workspace. That runner exposes private contract functions by inserting export lines into temporary copies of handler_artifacts and handler_log. The functions' bodies are unchanged; the application/library and direct comparison both import those same copies. No test-only switches are added to lib/.

The full direct comparison has these boundaries:

Boundary Direct implementation Library implementation
Per-request context Proper-list check before entering the diagnostic label Log.withContext
API digest Server.bundleHashC; invalid input is an ordinary Result Artifacts.loadFrom
Request callback protocol checkedInvoke, with B.anyC input H.checkedRequest
Adapter digest/path Shared authenticated filesystem adapter Artifacts.withFilesystem
Load reply Exact shared Artifacts.loadReplyC Same contract
Initial logging event Exact shared Log.eventC Log.emit
Decorated event Same contract after context concatenation Log.withSink
Logging storage Explicit sequential source state, ordinary Result unit recordLog through the test observer
Logging reply Exact shared Log.replyC Log.emit reply check
Application policy Redacted 404, invalid-digest 400, terminal logger failure Demo GET consumer
Final observation Execution Result containing all responses and logs Same output

Full-profile malformed-input/reply probes compare independently specified execution diagnostics AND read/log traces. These include invalid contexts, invalid digests, octets above 255, noncanonical naturals, improper byte lists, invalid Result envelopes/remainders, and ordinary read/log failures. The facade's mocked read service is deliberately separate from the real raw-reader boundary; permission-denial and raw invalid-digest controls exercise the latter.

Profiles are [API-digest body-bytes context-list event-shape sink-reply] flags. Disabled contract callbacks use B.anyC or the test-local envelope-only contract. This leaves callback/envelope plumbing in place rather than treating removal as an isolated cycle count for a predicate. The all-off profile still checks digests at the filesystem boundary and still checks service Result shape/remainder. The raw-reader counterexample runs without IO permission: invalid path input must be rejected before requesting filesystem access.

The profiles intentionally cease to satisfy particular library invariants; their counterexamples document that fact. They are never used as production substitutes or to update expected behavior of the full library. Handler lookup, lifetime, resource ownership, and security authority are not altered by this experiment.

Reproduction and measurement boundaries are in Testing. Generated measurements and investigation notes belong outside the repository's docs/, not in this fixture guide.

Validator optimization controls

The cost runner also exposes byteC and bytesC in its temporary workspace and checks batched results against Python-constructed structural domains. It covers all canonical octets, canonical numerals through 1024, all tree shapes through seven nodes, and mutations with invalid bits, redundant high zeros and improper tails. Digest checks cover every octet at the first, middle and last positions, length boundaries and malformed tails. Contracts must preserve both accepted values and a nonempty supplied remainder, including first-error diagnostics. validator_cases counts individual results separately from subprocess runs.

hashDemand supplies deliberately divergent terms as the 65th element/suffix, and after an invalid first character. These must remain unforced. The process guard bounds an accidental demand regression. These are rejection controls, not claims that validation terminates on every arbitrary computation supplied as data.

The optimized octet scanner checks exact Boolean bits and canonical termination through eight fixed continuation steps; it never calls unchecked arithmetic on input. The byte-list contract scans without rebuilding the accepted list or creating a Result for every successful element. This specialization is valid because the octet contract does not transform values; it is not a replacement for general listOf, whose transformation/proxy behavior remains necessary.

The digest predicate checks disjoint structural ranges for the sixteen lowercase hex characters and uses fixed unary fuel at the 64-character boundary. The explicit-length bundleHashBytes helper retains its numeric-counter interface. Both the API check and the raw filesystem adapter check remain enabled.

Large response controls

validateLengths checks the specialized HTTP body-spine count; the Python runner also checks the original general-purpose length against the same independent expectations. Arbitrary small trees, malformed list tails, canonical numerals used as lists, and lists crossing binary count boundaries are included. length_cases is separate from octet/digest validation counts. lengthDemand ensures elements are not evaluated, including before an improper-tail failure. The specialized count preserves length's not a list Result on malformed spines; it does not assert that elements are octets. Octet checks remain at the artifact boundaries and host send encoding.

The HTTP count forces each successor's outer constructor before traversing the next list cell. This avoids a per-element successor continuation chain and the transformed list required by general listOf; general collection contracts and base.length are unchanged. The body is still materialized, not streamed.

The stdout sink test checks both its Result and the actual rendered status line. A successful sink Result without the line is not accepted as a logging oracle.

Streaming upload demand boundaries

The opt-in handler_upload producer protocol uses native incremental staging, not a source list of chunks. streamChunks tests the source facade with small preconstructed chunks; it is not the memory benchmark. Native UploadTests exercise canonical decoding, exact counts, owner/lifecycle rules, atomic rename, permissions, abandonment and teardown. streaming_protocol.py exercises real HTTP fragmentation, EOF, staging visibility and admission failure.

The streaming benchmark exposed retention in the handler interpreter itself: lazy popped-frame tail projections and unused diagnostic label spines could retain consumed request contexts. The interpreter now demands the popped tail's outer list constructor and settles label list spines at host-IO boundaries. It does not force label payloads or unselected continuations. Dynamic divergent payload/continuation controls in library.tri verify those demand boundaries. This changes evaluation demand for interpreter bookkeeping, not lexical handler selection, forwarding, scope restoration, or ordinary Result semantics.

The bounded live test enforces both RSS and GC-sampled live-heap limits. A body protocol with small chunks but growing continuation history is not accepted as bounded merely because its native writer only retains one buffer.