diff --git a/lib/haskell/conformance.sh b/lib/haskell/conformance.sh index 4c64ea32..c6e0579f 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 partial statistics newton wordfreq mapgraph) +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 statistics newton wordfreq mapgraph uniquewords setops) PASS_COUNTS=() FAIL_COUNTS=() diff --git a/lib/haskell/tests/program-setops.sx b/lib/haskell/tests/program-setops.sx new file mode 100644 index 00000000..017013ca --- /dev/null +++ b/lib/haskell/tests/program-setops.sx @@ -0,0 +1,61 @@ +;; setops.hs — set union/intersection/difference on integer sets. +;; +;; Exercises Phase 12: `import qualified Data.Set as Set`, all three +;; combining operations + isSubsetOf. + +(define + hk-setops-source + "import qualified Data.Set as Set\n\ns1 = Set.insert 1 (Set.insert 2 (Set.insert 3 Set.empty))\ns2 = Set.insert 3 (Set.insert 4 (Set.insert 5 Set.empty))\ns3 = Set.insert 1 (Set.insert 2 Set.empty)\n") + +(hk-test + "setops.hs — union size = 5" + (hk-deep-force + (hk-run (str hk-setops-source "main = Set.size (Set.union s1 s2)\n"))) + 5) + +(hk-test + "setops.hs — intersection size = 1" + (hk-deep-force + (hk-run + (str hk-setops-source "main = Set.size (Set.intersection s1 s2)\n"))) + 1) + +(hk-test + "setops.hs — intersection contains 3" + (hk-deep-force + (hk-run + (str hk-setops-source "main = Set.member 3 (Set.intersection s1 s2)\n"))) + (list "True")) + +(hk-test + "setops.hs — difference s1 s2 size = 2" + (hk-deep-force + (hk-run (str hk-setops-source "main = Set.size (Set.difference s1 s2)\n"))) + 2) + +(hk-test + "setops.hs — difference doesn't contain shared key" + (hk-deep-force + (hk-run + (str hk-setops-source "main = Set.member 3 (Set.difference s1 s2)\n"))) + (list "False")) + +(hk-test + "setops.hs — s3 is subset of s1" + (hk-deep-force + (hk-run (str hk-setops-source "main = Set.isSubsetOf s3 s1\n"))) + (list "True")) + +(hk-test + "setops.hs — s1 not subset of s3" + (hk-deep-force + (hk-run (str hk-setops-source "main = Set.isSubsetOf s1 s3\n"))) + (list "False")) + +(hk-test + "setops.hs — empty set is subset of anything" + (hk-deep-force + (hk-run (str hk-setops-source "main = Set.isSubsetOf Set.empty s1\n"))) + (list "True")) + +{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail} diff --git a/lib/haskell/tests/program-uniquewords.sx b/lib/haskell/tests/program-uniquewords.sx new file mode 100644 index 00000000..ae24c745 --- /dev/null +++ b/lib/haskell/tests/program-uniquewords.sx @@ -0,0 +1,35 @@ +;; uniquewords.hs — count unique words using Data.Set. +;; +;; Exercises Phase 12: `import qualified Data.Set as Set`, `Set.empty`, +;; `Set.insert`, `Set.size`, `foldl`. + +(define + hk-uniquewords-source + "import qualified Data.Set as Set\n\naddWord s w = Set.insert w s\n\nuniqueWords ws = foldl addWord Set.empty ws\n\nresult = uniqueWords [\"the\", \"cat\", \"the\", \"dog\", \"the\", \"cat\"]\n") + +(hk-test + "uniquewords.hs — unique count = 3" + (hk-deep-force + (hk-run (str hk-uniquewords-source "main = Set.size result\n"))) + 3) + +(hk-test + "uniquewords.hs — \"the\" present" + (hk-deep-force + (hk-run (str hk-uniquewords-source "main = Set.member \"the\" result\n"))) + (list "True")) + +(hk-test + "uniquewords.hs — \"missing\" absent" + (hk-deep-force + (hk-run (str hk-uniquewords-source "main = Set.member \"missing\" result\n"))) + (list "False")) + +(hk-test + "uniquewords.hs — empty list yields empty set" + (hk-deep-force + (hk-run + "import qualified Data.Set as Set\nmain = Set.size (foldl (\\s w -> Set.insert w s) Set.empty [])")) + 0) + +{: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 bf80dea5..dac15562 100644 --- a/plans/haskell-completeness.md +++ b/plans/haskell-completeness.md @@ -213,7 +213,7 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. - [x] Unit tests in `lib/haskell/tests/set.sx` (17/17, plan ≥15: empty, insert, member hit/miss, delete, fromList deduplication, union, intersection, difference, isSubsetOf, plus 4 end-to-end via `import qualified Data.Set`). -- [ ] Conformance programs: +- [x] Conformance programs: - `uniquewords.hs` — unique words in a string using `Data.Set`. - `setops.hs` — set union/intersection/difference on integer sets; exercises all three combining operations. @@ -307,6 +307,14 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. _Newest first._ +**2026-05-07** — Phase 12 conformance: uniquewords.hs (4/4) + setops.hs (8/8) → Phase 12 complete: +- `program-uniquewords.sx`: `foldl Set.insert` over a word list, then check + `Set.size`/`member`. 4/4. +- `program-setops.sx`: full set algebra — union/intersection/difference/ + isSubsetOf with three sets s1, s2, s3 chosen so each operation has both a + positive and negative test. 8/8. +- Both added to `PROGRAMS` in `conformance.sh`. Phase 12 fully complete. + **2026-05-07** — Phase 12 unit tests `tests/set.sx` (17/17): - 13 SX-level direct calls + 4 end-to-end via `import qualified Data.Set`. Covers all the API + dedupe behavior. Suite is 17/17.