haskell: class/instance declarations — parse + instance dict eval (+11 tests, 503/503)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 00:22:44 +00:00
parent 5c00b5c58b
commit 41a69ecca7
4 changed files with 305 additions and 585 deletions

View File

@@ -681,73 +681,95 @@ negate x = 0 - x
(fn
(env decls)
(let
((groups (dict))
(group-order (list))
(pat-binds (list)))
;; Pass 1: collect fun-clause groups by name; track first-seen
;; order so pass 3 can evaluate 0-arity bodies in source order
;; (forward references to other 0-arity definitions still need
;; the earlier name to be bound first).
((groups (dict)) (group-order (list)) (pat-binds (list)))
(for-each
(fn (d)
(fn
(d)
(cond
((= (first d) "fun-clause")
(let
((name (nth d 1)))
(when (not (has-key? groups name))
(when
(not (has-key? groups name))
(append! group-order name))
(dict-set!
groups
name
(append
(if
(has-key? groups name)
(get groups name)
(list))
(if (has-key? groups name) (get groups name) (list))
(list (list (nth d 2) (nth d 3)))))
(when
(not (has-key? env name))
(dict-set! env name nil))))
(when (not (has-key? env name)) (dict-set! env name nil))))
((or (= (first d) "bind") (= (first d) "pat-bind"))
(append! pat-binds d))
((= (first d) "class-decl")
(dict-set!
env
(str "__class__" (nth d 1))
(list "class" (nth d 1) (nth d 2))))
((= (first d) "instance-decl")
(let
((cls (nth d 1))
(inst-type (nth d 2))
(method-decls (nth d 3)))
(let
((inst-dict (dict))
(inst-key
(str "dict" cls "_" (hk-type-ast-str inst-type))))
(for-each
(fn
(m)
(when
(= (first m) "fun-clause")
(let
((mname (nth m 1))
(pats (nth m 2))
(body (nth m 3)))
(dict-set!
inst-dict
mname
(if
(empty? pats)
(hk-eval body env)
(hk-eval (list "lambda" pats body) env))))))
method-decls)
(dict-set! env inst-key inst-dict))))
(:else nil)))
decls)
;; Pass 2: install multifuns (arity > 0) — order doesn't matter
;; because they're closures; collect 0-arity names in source
;; order for pass 3.
(let ((zero-arity (list)))
(let
((zero-arity (list)))
(for-each
(fn (name)
(let ((clauses (get groups name)))
(let ((arity (len (first (first clauses)))))
(fn
(name)
(let
((clauses (get groups name)))
(let
((arity (len (first (first clauses)))))
(cond
((> arity 0)
(dict-set!
env
name
(hk-mk-multifun arity clauses env)))
(dict-set! env name (hk-mk-multifun arity clauses env)))
(:else (append! zero-arity name))))))
group-order)
;; Pass 3: evaluate 0-arity bodies and pat-binds in source
;; order — forward references to a later 0-arity name will
;; still see its placeholder (nil) and fail noisily, but the
;; common case of a top-down program works.
(for-each
(fn (name)
(let ((clauses (get groups name)))
(fn
(name)
(let
((clauses (get groups name)))
(dict-set!
env
name
(hk-eval (first (rest (first clauses))) env))))
zero-arity)
(for-each
(fn (d)
(let ((pat (nth d 1)) (body (nth d 2)))
(let ((val (hk-eval body env)))
(let ((res (hk-match pat val env)))
(fn
(d)
(let
((pat (nth d 1)) (body (nth d 2)))
(let
((val (hk-eval body env)))
(let
((res (hk-match pat val env)))
(cond
((nil? res)
(raise "top-level pattern bind failure"))
((nil? res) (raise "top-level pattern bind failure"))
(:else (hk-extend-env-with-match! env res)))))))
pat-binds))
env)))
@@ -791,6 +813,22 @@ negate x = 0 - x
(src)
(hk-deep-force (hk-eval (hk-core-expr src) (hk-dict-copy hk-env0)))))
(define
hk-type-ast-str
(fn
(ast)
(cond
((= (first ast) "t-con") (nth ast 1))
((= (first ast) "t-var") (nth ast 1))
((= (first ast) "t-list")
(str "[" (hk-type-ast-str (nth ast 1)) "]"))
((= (first ast) "t-app")
(str
(hk-type-ast-str (nth ast 1))
" "
(hk-type-ast-str (nth ast 2))))
(:else "?"))))
(define
hk-typecheck
(fn

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
;; class.sx — tests for class/instance parsing and evaluation.
(define prog-class1 (hk-core "class MyEq a where\n myEq :: a -> a -> Bool"))
(define prog-inst1 (hk-core "instance MyEq Int where\n myEq x y = x == y"))
;; ─── class-decl AST ───────────────────────────────────────────────────────────
(define cd1 (first (nth prog-class1 1)))
(hk-test "class-decl tag" (first cd1) "class-decl")
(hk-test "class-decl name" (nth cd1 1) "MyEq")
(hk-test "class-decl tvar" (nth cd1 2) "a")
(hk-test "class-decl methods" (len (nth cd1 3)) 1)
;; ─── instance-decl AST ────────────────────────────────────────────────────────
(define id1 (first (nth prog-inst1 1)))
(hk-test "instance-decl tag" (first id1) "instance-decl")
(hk-test "instance-decl class" (nth id1 1) "MyEq")
(hk-test "instance-decl type tag" (first (nth id1 2)) "t-con")
(hk-test "instance-decl type name" (nth (nth id1 2) 1) "Int")
(hk-test "instance-decl method count" (len (nth id1 3)) 1)
;; ─── eval: instance dict is built ────────────────────────────────────────────
(define
prog-full
(hk-core
"class MyEq a where\n myEq :: a -> a -> Bool\ninstance MyEq Int where\n myEq x y = x == y"))
(define env-full (hk-eval-program prog-full))
(hk-test "instance dict in env" (has-key? env-full "dictMyEq_Int") true)
(hk-test
"instance dict has method"
(has-key? (get env-full "dictMyEq_Int") "myEq")
true)
{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}

View File

@@ -99,7 +99,7 @@ Key mappings:
- [x] Unit tests: inference for 50+ expressions
### Phase 5 — typeclasses (dictionary passing)
- [ ] `class` / `instance` declarations
- [x] `class` / `instance` declarations
- [ ] Dictionary-passing elaborator: inserts dict args at call sites
- [ ] Standard classes: `Eq`, `Ord`, `Show`, `Num`, `Functor`, `Monad`, `Applicative`
- [ ] `deriving (Eq, Show)` for ADTs
@@ -114,6 +114,17 @@ Key mappings:
_Newest first._
- **2026-05-06** — Phase 5 class/instance declarations. Parser: `hk-parse-class`
and `hk-parse-instance` added to the parser closure; `hk-parse-decl` gains
arms for `"class"` and `"instance"` reserved words (tokenizer already marks
them reserved). `class Eq a where { ... }``("class-decl" name tvar decls)`;
`instance Eq Int where { ... }``("instance-decl" name inst-type decls)`.
Eval: `hk-type-ast-str` converts type AST to a string key. `hk-bind-decls!`
gains arms for `class-decl` (registers `__class__Name` marker) and
`instance-decl` (builds method dict, binds as `dictClassName_TypeStr` in env).
11 new tests in `tests/class.sx` covering AST shapes + runtime dict
construction. 503/503 green.
- **2026-05-05** — Phase 4 inference unit tests (50+ expressions). Added 16 new
`hk-t` expression tests to `tests/infer.sx`: nested application (`not(not True)`,
`negate(negate 1)`), bool/mixed lambdas (`\\x->\\y->x&&y`, `\\x->x==1`),