Parser hk-parse-parens gains a `::` arm after the first inner expression:
consume `::`, parse a type via the existing hk-parse-type, expect `)`,
emit (:type-ann EXPR TYPE). Sections, tuples, parenthesised expressions
and unit `()` are unchanged.
Desugar drops the annotation — :type-ann E _ → (hk-desugar E) — since
the existing eval path has no type-directed dispatch. Phase 20 will
extend infer.sx to consume the annotation and unify against the
inferred type.
tests/parse-extras.sx (12/12) covers literal, arithmetic, function arg,
string, bool, tuple, nested annotation, function-typed annotation, and
no-regression checks for plain parens / 3-tuples / left+right sections.
eval (66/0), exceptions (14/0), typecheck (15/0), records (14/0), ioref
(13/0) all still clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five "typed ok: …" tests in tests/typecheck.sx compared an unforced thunk
against an integer/list. The untyped-path convention is hk-deep-force on
the result; hk-run-typed follows the same shape but the tests omitted
that wrap. Added hk-deep-force around hk-run-typed in those five tests.
typecheck.sx now 15/15; infer.sx still 75/75.
Plan adds three phases capturing the remaining type-system work:
- Phase 20: Algorithm W gaps (case, do, record accessors, expression
annotations).
- Phase 21: type classes with qualified types ([Eq a] => …) and
constraint propagation, integrated with the existing dict-passing
evaluator.
- Phase 22: typecheck-then-run as the default conformance path, with a
≥ 30/36 typechecking threshold before swap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings the architecture branch (559 commits ahead — R7RS step 4-6, JIT
expansion, host_error wrapping, bytecode compiler, etc.) into the
loops/haskell line of work. Conflict in lib/haskell/conformance.sh:
architecture replaced the inline driver with a thin wrapper delegating
to lib/guest/conformance.sh + a config file. Resolved by taking the
wrapper and extending lib/haskell/conformance.conf with all programs
added under loops/haskell (caesar, runlength-str, showadt, showio,
partial, statistics, newton, wordfreq, mapgraph, uniquewords, setops,
shapes, person, config, counter, accumulate, safediv, trycatch) plus
adding map.sx and set.sx to PRELOADS.
plans/haskell-completeness.md gains three new follow-up phases:
- Phase 17 — parser polish (`(x :: Int)` annotations, mid-file imports)
- Phase 18 — one ambitious conformance program (lambda-calc / Dijkstra /
JSON parser candidate list)
- Phase 19 — conformance speed (batch all suites in one sx_server
process to compress the 25-min run to single-digit minutes)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hk-bind-exceptions! in eval.sx registers throwIO, throw, evaluate, catch,
try, handle, displayException. SomeException constructor pre-registered
in runtime.sx (arity 1, type SomeException).
throwIO and the existing error primitive both raise via SX `raise` with a
uniform "hk-error: msg" string. catch/try/handle parse it back into a
SomeException via hk-exception-of, which strips nested
'Unhandled exception: "..."' host wraps (CEK's host_error formatter) and
the "hk-error: " prefix.
catch and handle evaluate the handler outside the guard scope (build an
"ok"/"exn" outcome tag inside guard, then dispatch outside) so that a
re-throw from the handler propagates past this catch — matching Haskell
semantics rather than infinite-looping in the same guard.
14 unit tests in tests/exceptions.sx (catch success, catch error, try
Right/Left, handle, throwIO + catch/try, evaluate, nested catch, do-bind
through catch, branch on try result, IORef-mutating handler).
Conformance: safediv.hs (8/8) and trycatch.hs (8/8). Scoreboard now
285/285 tests, 36/36 programs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Haskell strings are [Char]. Calling reverse / head / length on a SX raw
string transparently produces a cons-list of char codes (via hk-str-head /
hk-str-tail in runtime.sx), but (==) then compared the original raw string
against the char-code cons-list and always returned False — so
"racecar" == reverse "racecar" was False.
Added hk-try-charlist-to-string and hk-normalize-for-eq in eval.sx; routed
== and /= through hk-normalize-for-eq so a string compares equal to any
cons-list whose elements are valid Unicode code points spelling the same
characters, and "[]" ↔ "".
palindrome.hs lifts from 9/12 → 12/12; conformance 33/34 → 34/34 programs,
266/269 → 269/269 tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hk-bind-data-ioref! registers newIORef / readIORef / writeIORef /
modifyIORef / modifyIORef' under the import alias (default IORef).
Representation: dict {"hk-ioref" true "hk-value" v} allocated inside IO.
modifyIORef' uses hk-deep-force on the new value before write.
Side-effect: fixed pre-existing bug in import handler — modname was
reading (nth d 1) (the qualified flag) instead of (nth d 2). All
'import qualified … as Foo' paths were silently no-ops; map.sx unit
suite jumps from 22→26 passing.
Conformance now 33/34 programs, 266/269 tests (only pre-existing
palindrome.hs 9/12 still failing on string-as-list reversal, present
on prior commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>