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
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:
@@ -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
35
lib/haskell/tests/class.sx
Normal file
35
lib/haskell/tests/class.sx
Normal 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}
|
||||
@@ -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`),
|
||||
|
||||
Reference in New Issue
Block a user