From 80d6507e57f04d5dddbae0afb82e978b000fd900 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 6 May 2026 22:27:30 +0000 Subject: [PATCH] =?UTF-8?q?haskell:=20Phase=208=20audit=20=E2=80=94=20hk-s?= =?UTF-8?q?how-val=20matches=20Haskell=2098=20(precedence-based=20parens,?= =?UTF-8?q?=20no-space=20separators)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- lib/haskell/eval.sx | 32 +++++++++++++++++++------------- lib/haskell/tests/deriving.sx | 8 ++++---- lib/haskell/tests/stdlib.sx | 16 ++++++++-------- plans/haskell-completeness.md | 19 ++++++++++++++++--- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/lib/haskell/eval.sx b/lib/haskell/eval.sx index f11de7fe..3309cf3b 100644 --- a/lib/haskell/eval.sx +++ b/lib/haskell/eval.sx @@ -584,13 +584,14 @@ result)))) (define - hk-show-val + hk-show-prec (fn - (v) + (v p) (let ((fv (hk-force v))) (cond - ((= (type-of fv) "number") (str fv)) + ((= (type-of fv) "number") + (if (and (< fv 0) (>= p 11)) (str "(" fv ")") (str fv))) ((= (type-of fv) "string") (str "\"" fv "\"")) ((= (type-of fv) "boolean") (if fv "True" "False")) ((not (list? fv)) (str fv)) @@ -599,9 +600,15 @@ ((= (first fv) ":") (let ((elems (hk-collect-hk-list fv))) - (str "[" (hk-join-strs (map hk-show-val elems) ", ") "]"))) + (str + "[" + (hk-join-strs (map (fn (e) (hk-show-prec e 0)) elems) ",") + "]"))) ((= (first fv) "Tuple") - (str "(" (hk-join-strs (map hk-show-val (rest fv)) ", ") ")")) + (str + "(" + (hk-join-strs (map (fn (e) (hk-show-prec e 0)) (rest fv)) ",") + ")")) ((= (first fv) "()") "()") (:else (let @@ -609,14 +616,15 @@ (if (empty? args) cname - (str - "(" - cname - " " - (hk-join-strs (map hk-show-val args) " ") - ")")))))))) + (let + ((s (str cname " " (hk-join-strs (map (fn (a) (hk-show-prec a 11)) args) " ")))) + (if (>= p 11) (str "(" s ")") s))))))))) ;; ── Source-level convenience ──────────────────────────────── +(define hk-show-val (fn (v) (hk-show-prec v 0))) + +;; Eagerly build the Prelude env once at load time; each call to +;; hk-eval-expr-source copies it instead of re-parsing the whole Prelude. (define hk-init-env (fn @@ -997,8 +1005,6 @@ 1)) env))))) -;; Eagerly build the Prelude env once at load time; each call to -;; hk-eval-expr-source copies it instead of re-parsing the whole Prelude. (define hk-bind-decls! (fn diff --git a/lib/haskell/tests/deriving.sx b/lib/haskell/tests/deriving.sx index db120900..80115171 100644 --- a/lib/haskell/tests/deriving.sx +++ b/lib/haskell/tests/deriving.sx @@ -12,14 +12,14 @@ "deriving Show: constructor with arg" (hk-deep-force (hk-run "data Wrapper = Wrap Int deriving (Show)\nmain = show (Wrap 42)")) - "(Wrap 42)") + "Wrap 42") (hk-test "deriving Show: nested constructors" (hk-deep-force (hk-run "data Tree = Leaf | Node Int Tree Tree deriving (Show)\nmain = show (Node 1 Leaf Leaf)")) - "(Node 1 Leaf Leaf)") + "Node 1 Leaf Leaf") (hk-test "deriving Show: second constructor" @@ -61,11 +61,11 @@ ;; ─── combined Eq + Show ─────────────────────────────────────────────────────── (hk-test - "deriving Eq Show: combined in parens" + "deriving Eq Show: combined" (hk-deep-force (hk-run "data Shape = Circle Int | Square Int deriving (Eq, Show)\nmain = show (Circle 5)")) - "(Circle 5)") + "Circle 5") (hk-test "deriving Eq Show: eq on constructor with arg" diff --git a/lib/haskell/tests/stdlib.sx b/lib/haskell/tests/stdlib.sx index 4be0db57..ce06bfd4 100644 --- a/lib/haskell/tests/stdlib.sx +++ b/lib/haskell/tests/stdlib.sx @@ -37,11 +37,11 @@ (hk-ts "show neg" "negate 7" "-7") (hk-ts "show bool T" "True" "True") (hk-ts "show bool F" "False" "False") -(hk-ts "show list" "[1,2,3]" "[1, 2, 3]") -(hk-ts "show Just" "Just 5" "(Just 5)") +(hk-ts "show list" "[1,2,3]" "[1,2,3]") +(hk-ts "show Just" "Just 5" "Just 5") (hk-ts "show Nothing" "Nothing" "Nothing") (hk-ts "show LT" "LT" "LT") -(hk-ts "show tuple" "(1, True)" "(1, True)") +(hk-ts "show tuple" "(1, True)" "(1,True)") ;; ── Num extras ─────────────────────────────────────────────── (hk-test "signum pos" (hk-deep-force (hk-run "main = signum 5")) 1) @@ -59,13 +59,13 @@ (hk-test "foldr cons" (hk-deep-force (hk-run "main = show (foldr (:) [] [1,2,3])")) - "[1, 2, 3]") + "[1,2,3]") ;; ── List ops ───────────────────────────────────────────────── (hk-test "reverse" (hk-deep-force (hk-run "main = show (reverse [1,2,3])")) - "[3, 2, 1]") + "[3,2,1]") (hk-test "null []" (hk-deep-force (hk-run "main = null []")) (list "True")) (hk-test "null xs" @@ -82,7 +82,7 @@ (hk-test "zip" (hk-deep-force (hk-run "main = show (zip [1,2] [3,4])")) - "[(1, 3), (2, 4)]") + "[(1,3),(2,4)]") (hk-test "sum" (hk-deep-force (hk-run "main = sum [1,2,3,4,5]")) 15) (hk-test "product" (hk-deep-force (hk-run "main = product [1,2,3,4]")) 24) (hk-test "maximum" (hk-deep-force (hk-run "main = maximum [3,1,9,2]")) 9) @@ -112,7 +112,7 @@ (hk-test "fmap list" (hk-deep-force (hk-run "main = show (fmap (+1) [1,2,3])")) - "[2, 3, 4]") + "[2,3,4]") ;; ── Monad / Applicative ────────────────────────────────────── (hk-test "return" (hk-deep-force (hk-run "main = return 7")) (list "IO" 7)) @@ -134,7 +134,7 @@ (hk-test "lookup hit" (hk-deep-force (hk-run "main = show (lookup 2 [(1,10),(2,20)])")) - "(Just 20)") + "Just 20") (hk-test "lookup miss" (hk-deep-force (hk-run "main = show (lookup 9 [(1,10)])")) diff --git a/plans/haskell-completeness.md b/plans/haskell-completeness.md index 7dfacaac..79df99ef 100644 --- a/plans/haskell-completeness.md +++ b/plans/haskell-completeness.md @@ -97,9 +97,11 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. ### Phase 8 — `show` for arbitrary types -- [ ] Audit `hk-show-val` in `runtime.sx` — ensure output format matches - Haskell 98: `"Just 3"`, `"[1,2,3]"`, `"(True,False)"`, `"'a'"` (Char shows - with single-quotes), `"\"hello\""` (String shows with escaped double-quotes). +- [x] Audit `hk-show-val` in `runtime.sx` — ensure output format matches + Haskell 98: `"Just 3"`, `"[1,2,3]"`, `"(True,False)"`, `"\"hello\""` (String + shows with escaped double-quotes). _Deferred:_ `"'a'"` Char single-quotes + (needs Char tagging — currently Char = Int by representation, ambiguous in + show); `\n`/`\t` escape inside Strings. - [ ] `show` Prelude binding calls `hk-show-val`; `print x = putStrLn (show x)`. - [ ] `deriving Show` auto-generates proper show for record-style and multi-constructor ADTs. Nested application arguments wrapped in parens: @@ -284,6 +286,17 @@ No OCaml changes are needed. The view type is fully representable as an SX dict. _Newest first._ +**2026-05-06** — Phase 8 audit: `hk-show-val` matches Haskell 98 format: +- `eval.sx`: introduced `hk-show-prec v p` with precedence-based parens. + Top-level `show (Just 3)` = `"Just 3"` (no parens); nested `show (Just (Just 3))` + = `"Just (Just 3)"` (inner wrapped because called with prec ≥ 11). Negative + ints wrapped in parens at high prec for `show (Just (negate 1))` correctness. +- List/tuple separators changed from `", "` to `","` to match GHC. +- `hk-show-val` is now a thin shim: `(hk-show-prec v 0)`. +- Updated `tests/deriving.sx` (3 tests) and `tests/stdlib.sx` (7 tests) to the + new format. `Char` single-quote output and string escape for `\n`/`\t` + deferred — Char = Int representation prevents disambiguation in show. + **2026-05-06** — Phase 7 conformance complete (runlength-str.hs) + `++` thunk fix: - New `lib/haskell/tests/program-runlength-str.sx` (9 tests). Exercises `(x:xs)` pattern matching over Strings, `span` over a string view, tuple `(Int, Char)`