diff --git a/lib/haskell/conformance.sh b/lib/haskell/conformance.sh index 75c451df..c0c542dd 100755 --- a/lib/haskell/conformance.sh +++ b/lib/haskell/conformance.sh @@ -20,7 +20,7 @@ if [ ! -x "$SX_SERVER" ]; then fi fi -PROGRAMS=(fib sieve quicksort nqueens calculator collatz palindrome maybe fizzbuzz anagram roman binary either primes zipwith matrix wordcount powers caesar runlength-str showadt showio) +PROGRAMS=(fib sieve quicksort nqueens calculator collatz palindrome maybe fizzbuzz anagram roman binary either primes zipwith matrix wordcount powers caesar runlength-str showadt showio partial) PASS_COUNTS=() FAIL_COUNTS=() diff --git a/lib/haskell/tests/program-partial.sx b/lib/haskell/tests/program-partial.sx new file mode 100644 index 00000000..f14dc93e --- /dev/null +++ b/lib/haskell/tests/program-partial.sx @@ -0,0 +1,58 @@ +;; partial.hs — exercises Phase 9 partial functions caught at the top level. +;; +;; Each program calls a partial function on bad input; hk-run-io catches the +;; raise and appends the error message to io-lines so tests can inspect. + +(hk-test + "partial.hs — main = print (head [])" + (let + ((lines (hk-run-io "main = print (head [])"))) + (>= (index-of (str lines) "Prelude.head: empty list") 0)) + true) + +(hk-test + "partial.hs — main = print (tail [])" + (let + ((lines (hk-run-io "main = print (tail [])"))) + (>= (index-of (str lines) "Prelude.tail: empty list") 0)) + true) + +(hk-test + "partial.hs — main = print (fromJust Nothing)" + (let + ((lines (hk-run-io "main = print (fromJust Nothing)"))) + (>= (index-of (str lines) "Maybe.fromJust: Nothing") 0)) + true) + +(hk-test + "partial.hs — putStrLn before error preserves prior output" + (let + ((lines (hk-run-io "main = do { putStrLn \"step 1\"; putStrLn (show (head [])); putStrLn \"never\" }"))) + (and + (>= (index-of (str lines) "step 1") 0) + (>= (index-of (str lines) "Prelude.head: empty list") 0) + (= (index-of (str lines) "never") -1))) + true) + +(hk-test + "partial.hs — undefined as IO action" + (let + ((lines (hk-run-io "main = print undefined"))) + (>= (index-of (str lines) "Prelude.undefined") 0)) + true) + +(hk-test + "partial.hs — catches error from a user-thrown error" + (let + ((lines (hk-run-io "main = error \"boom from main\""))) + (>= (index-of (str lines) "boom from main") 0)) + true) + +;; Negative case: when no error is raised, io-lines doesn't contain +;; "Prelude" prefixes from our error path. +(hk-test + "partial.hs — happy path: head [42] succeeds, no error in output" + (hk-run-io "main = print (head [42])") + (list "42")) + +{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail} diff --git a/plans/haskell-completeness.md b/plans/haskell-completeness.md index c33ec445..8e49aef2 100644 --- a/plans/haskell-completeness.md +++ b/plans/haskell-completeness.md @@ -138,7 +138,7 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. an `hk-error` whose message contains the given substring. - [x] Tests in `lib/haskell/tests/errors.sx` (≥ 10 tests: error message content, undefined, head/tail/fromJust on bad input, `hk-test-error` helper). -- [ ] Conformance programs: +- [x] Conformance programs: - `partial.hs` — exercises `head []`, `tail []`, `fromJust Nothing` caught at the top level; shows error messages. @@ -293,6 +293,14 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. _Newest first._ +**2026-05-07** — Phase 9 conformance: `partial.hs` (7/7) → Phase 9 complete: +- New `tests/program-partial.sx` exercising `head []`, `tail []`, + `fromJust Nothing`, `undefined`, and user `error` from inside a `do` block; + verifies the error message lands in `hk-run-io`'s `io-lines`. Also a happy- + path test (`head [42] = 42`) and a "putStrLn before error preserves prior + output, never reaches subsequent action" test. +- Added `partial` to `PROGRAMS` in `conformance.sh`. Phase 9 done. + **2026-05-07** — Phase 9 `tests/errors.sx` (14/14): - New file with 14 tests covering: error w/ literal + computed message; error in `if` branch (laziness boundary); undefined via direct + forcing-via-