Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 46s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
596 lines
23 KiB
Plaintext
596 lines
23 KiB
Plaintext
;; infer.sx — Hindley-Milner Algorithm W for Haskell-on-SX (Phase 4).
|
||
;;
|
||
;; Types: TVar, TCon, TArr, TApp, TTuple, TScheme
|
||
;; Substitution: apply, compose, restrict
|
||
;; Unification (with occurs check)
|
||
;; Instantiation + generalization (let-polymorphism)
|
||
;; Algorithm W for: literals, var, con, lambda, app, let, if, op, tuple, list
|
||
|
||
;; ─── Type constructors ────────────────────────────────────────────────────────
|
||
|
||
(define hk-tvar (fn (n) (list "TVar" n)))
|
||
(define hk-tcon (fn (s) (list "TCon" s)))
|
||
(define hk-tarr (fn (a b) (list "TArr" a b)))
|
||
(define hk-tapp (fn (a b) (list "TApp" a b)))
|
||
(define hk-ttuple (fn (ts) (list "TTuple" ts)))
|
||
(define hk-tscheme (fn (vs t) (list "TScheme" vs t)))
|
||
|
||
(define hk-tvar? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TVar"))))
|
||
(define hk-tcon? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TCon"))))
|
||
(define hk-tarr? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TArr"))))
|
||
(define hk-tapp? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TApp"))))
|
||
(define hk-ttuple? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TTuple"))))
|
||
(define hk-tscheme? (fn (t) (and (list? t) (not (empty? t)) (= (first t) "TScheme"))))
|
||
|
||
(define hk-tvar-name (fn (t) (nth t 1)))
|
||
(define hk-tcon-name (fn (t) (nth t 1)))
|
||
(define hk-tarr-t1 (fn (t) (nth t 1)))
|
||
(define hk-tarr-t2 (fn (t) (nth t 2)))
|
||
(define hk-tapp-t1 (fn (t) (nth t 1)))
|
||
(define hk-tapp-t2 (fn (t) (nth t 2)))
|
||
(define hk-ttuple-ts (fn (t) (nth t 1)))
|
||
(define hk-tscheme-vs (fn (t) (nth t 1)))
|
||
(define hk-tscheme-type (fn (t) (nth t 2)))
|
||
|
||
(define hk-t-int (hk-tcon "Int"))
|
||
(define hk-t-bool (hk-tcon "Bool"))
|
||
(define hk-t-string (hk-tcon "String"))
|
||
(define hk-t-char (hk-tcon "Char"))
|
||
(define hk-t-float (hk-tcon "Float"))
|
||
(define hk-t-list (fn (t) (hk-tapp (hk-tcon "[]") t)))
|
||
|
||
;; ─── Type formatter ──────────────────────────────────────────────────────────
|
||
|
||
(define
|
||
hk-type->str
|
||
(fn
|
||
(t)
|
||
(cond
|
||
((hk-tvar? t) (hk-tvar-name t))
|
||
((hk-tcon? t) (hk-tcon-name t))
|
||
((hk-tarr? t)
|
||
(let ((s1 (if (hk-tarr? (hk-tarr-t1 t))
|
||
(str "(" (hk-type->str (hk-tarr-t1 t)) ")")
|
||
(hk-type->str (hk-tarr-t1 t)))))
|
||
(str s1 " -> " (hk-type->str (hk-tarr-t2 t)))))
|
||
((hk-tapp? t)
|
||
(let ((h (hk-tapp-t1 t)))
|
||
(cond
|
||
((and (hk-tcon? h) (= (hk-tcon-name h) "[]"))
|
||
(str "[" (hk-type->str (hk-tapp-t2 t)) "]"))
|
||
(:else
|
||
(str "(" (hk-type->str h) " " (hk-type->str (hk-tapp-t2 t)) ")")))))
|
||
((hk-ttuple? t)
|
||
(str "(" (join ", " (map hk-type->str (hk-ttuple-ts t))) ")"))
|
||
((hk-tscheme? t)
|
||
(str "forall " (join " " (hk-tscheme-vs t)) ". " (hk-type->str (hk-tscheme-type t))))
|
||
(:else "<?>"))))
|
||
|
||
;; ─── Fresh variable counter ───────────────────────────────────────────────────
|
||
|
||
(define hk-fresh-ctr 0)
|
||
(define hk-fresh (fn () (set! hk-fresh-ctr (+ hk-fresh-ctr 1)) (hk-tvar (str "t" hk-fresh-ctr))))
|
||
(define hk-reset-fresh (fn () (set! hk-fresh-ctr 0)))
|
||
|
||
;; ─── Utilities ───────────────────────────────────────────────────────────────
|
||
|
||
(define hk-infer-member? (fn (x lst) (some (fn (y) (= x y)) lst)))
|
||
|
||
(define
|
||
hk-nub
|
||
(fn (lst)
|
||
(reduce (fn (acc x) (if (hk-infer-member? x acc) acc (append acc (list x)))) (list) lst)))
|
||
|
||
;; ─── Free type variables ──────────────────────────────────────────────────────
|
||
|
||
(define
|
||
hk-ftv
|
||
(fn
|
||
(t)
|
||
(cond
|
||
((hk-tvar? t) (list (hk-tvar-name t)))
|
||
((hk-tcon? t) (list))
|
||
((hk-tarr? t) (append (hk-ftv (hk-tarr-t1 t)) (hk-ftv (hk-tarr-t2 t))))
|
||
((hk-tapp? t) (append (hk-ftv (hk-tapp-t1 t)) (hk-ftv (hk-tapp-t2 t))))
|
||
((hk-ttuple? t) (reduce append (list) (map hk-ftv (hk-ttuple-ts t))))
|
||
((hk-tscheme? t)
|
||
(filter
|
||
(fn (v) (not (hk-infer-member? v (hk-tscheme-vs t))))
|
||
(hk-ftv (hk-tscheme-type t))))
|
||
(:else (list)))))
|
||
|
||
(define
|
||
hk-ftv-env
|
||
(fn (env)
|
||
(reduce (fn (acc k) (append acc (hk-ftv (get env k)))) (list) (keys env))))
|
||
|
||
;; ─── Substitution ─────────────────────────────────────────────────────────────
|
||
|
||
(define hk-subst-empty (dict))
|
||
|
||
(define
|
||
hk-subst-restrict
|
||
(fn
|
||
(s exclude)
|
||
(let ((r (dict)))
|
||
(for-each
|
||
(fn (k)
|
||
(when (not (hk-infer-member? k exclude))
|
||
(dict-set! r k (get s k))))
|
||
(keys s))
|
||
r)))
|
||
|
||
(define
|
||
hk-subst-apply
|
||
(fn
|
||
(s t)
|
||
(cond
|
||
((hk-tvar? t)
|
||
(let ((v (get s (hk-tvar-name t))))
|
||
(if (nil? v) t (hk-subst-apply s v))))
|
||
((hk-tarr? t)
|
||
(hk-tarr (hk-subst-apply s (hk-tarr-t1 t))
|
||
(hk-subst-apply s (hk-tarr-t2 t))))
|
||
((hk-tapp? t)
|
||
(hk-tapp (hk-subst-apply s (hk-tapp-t1 t))
|
||
(hk-subst-apply s (hk-tapp-t2 t))))
|
||
((hk-ttuple? t)
|
||
(hk-ttuple (map (fn (u) (hk-subst-apply s u)) (hk-ttuple-ts t))))
|
||
((hk-tscheme? t)
|
||
(let ((s2 (hk-subst-restrict s (hk-tscheme-vs t))))
|
||
(hk-tscheme (hk-tscheme-vs t)
|
||
(hk-subst-apply s2 (hk-tscheme-type t)))))
|
||
(:else t))))
|
||
|
||
(define
|
||
hk-subst-compose
|
||
(fn
|
||
(s2 s1)
|
||
(let ((r (hk-dict-copy s2)))
|
||
(for-each
|
||
(fn (k)
|
||
(when (nil? (get r k))
|
||
(dict-set! r k (hk-subst-apply s2 (get s1 k)))))
|
||
(keys s1))
|
||
r)))
|
||
|
||
(define
|
||
hk-env-apply-subst
|
||
(fn
|
||
(s env)
|
||
(let ((r (dict)))
|
||
(for-each (fn (k) (dict-set! r k (hk-subst-apply s (get env k)))) (keys env))
|
||
r)))
|
||
|
||
;; ─── Unification ─────────────────────────────────────────────────────────────
|
||
|
||
(define
|
||
hk-bind-var
|
||
(fn
|
||
(v t)
|
||
(cond
|
||
((and (hk-tvar? t) (= (hk-tvar-name t) v))
|
||
hk-subst-empty)
|
||
((hk-infer-member? v (hk-ftv t))
|
||
(raise (str "Occurs check failed: " v " in " (hk-type->str t))))
|
||
(:else
|
||
(let ((s (dict)))
|
||
(dict-set! s v t)
|
||
s)))))
|
||
|
||
(define
|
||
hk-zip-unify
|
||
(fn
|
||
(ts1 ts2 acc)
|
||
(if (or (empty? ts1) (empty? ts2))
|
||
acc
|
||
(let ((s (hk-unify (hk-subst-apply acc (first ts1))
|
||
(hk-subst-apply acc (first ts2)))))
|
||
(hk-zip-unify (rest ts1) (rest ts2) (hk-subst-compose s acc))))))
|
||
|
||
(define
|
||
hk-unify
|
||
(fn
|
||
(t1 t2)
|
||
(cond
|
||
((and (hk-tvar? t1) (hk-tvar? t2) (= (hk-tvar-name t1) (hk-tvar-name t2)))
|
||
hk-subst-empty)
|
||
((hk-tvar? t1) (hk-bind-var (hk-tvar-name t1) t2))
|
||
((hk-tvar? t2) (hk-bind-var (hk-tvar-name t2) t1))
|
||
((and (hk-tcon? t1) (hk-tcon? t2) (= (hk-tcon-name t1) (hk-tcon-name t2)))
|
||
hk-subst-empty)
|
||
((and (hk-tarr? t1) (hk-tarr? t2))
|
||
(let ((s1 (hk-unify (hk-tarr-t1 t1) (hk-tarr-t1 t2))))
|
||
(let ((s2 (hk-unify (hk-subst-apply s1 (hk-tarr-t2 t1))
|
||
(hk-subst-apply s1 (hk-tarr-t2 t2)))))
|
||
(hk-subst-compose s2 s1))))
|
||
((and (hk-tapp? t1) (hk-tapp? t2))
|
||
(let ((s1 (hk-unify (hk-tapp-t1 t1) (hk-tapp-t1 t2))))
|
||
(let ((s2 (hk-unify (hk-subst-apply s1 (hk-tapp-t2 t1))
|
||
(hk-subst-apply s1 (hk-tapp-t2 t2)))))
|
||
(hk-subst-compose s2 s1))))
|
||
((and (hk-ttuple? t1) (hk-ttuple? t2)
|
||
(= (length (hk-ttuple-ts t1)) (length (hk-ttuple-ts t2))))
|
||
(hk-zip-unify (hk-ttuple-ts t1) (hk-ttuple-ts t2) hk-subst-empty))
|
||
(:else
|
||
(raise (str "Cannot unify " (hk-type->str t1) " with " (hk-type->str t2)))))))
|
||
|
||
;; ─── Instantiation and generalization ────────────────────────────────────────
|
||
|
||
(define
|
||
hk-instantiate
|
||
(fn
|
||
(t)
|
||
(if (not (hk-tscheme? t))
|
||
t
|
||
(let ((s (dict)))
|
||
(for-each (fn (v) (dict-set! s v (hk-fresh))) (hk-tscheme-vs t))
|
||
(hk-subst-apply s (hk-tscheme-type t))))))
|
||
|
||
(define
|
||
hk-generalize
|
||
(fn
|
||
(env t)
|
||
(let ((free-t (hk-nub (hk-ftv t)))
|
||
(free-env (hk-nub (hk-ftv-env env))))
|
||
(let ((bound (filter (fn (v) (not (hk-infer-member? v free-env))) free-t)))
|
||
(if (empty? bound)
|
||
t
|
||
(hk-tscheme bound t))))))
|
||
|
||
;; ─── Pattern binding extraction ──────────────────────────────────────────────
|
||
;; Returns a dict of name → type bindings introduced by matching pat against tv.
|
||
|
||
(define
|
||
hk-w-pat
|
||
(fn
|
||
(pat tv)
|
||
(let ((tag (first pat)))
|
||
(cond
|
||
((= tag "p-var") (let ((d (dict))) (dict-set! d (nth pat 1) tv) d))
|
||
((= tag "p-wild") (dict))
|
||
(:else (dict))))))
|
||
|
||
;; ─── Algorithm W ─────────────────────────────────────────────────────────────
|
||
;; hk-w : env × expr → (list subst type)
|
||
|
||
(define
|
||
hk-w-let
|
||
(fn
|
||
(env binds body)
|
||
;; Infer types for each binding in order, generalising at each step.
|
||
(let
|
||
((env2
|
||
(reduce
|
||
(fn
|
||
(cur-env b)
|
||
(let ((tag (first b)))
|
||
(cond
|
||
;; Simple pattern binding: let x = expr
|
||
((or (= tag "bind") (= tag "pat-bind"))
|
||
(let ((pat (nth b 1))
|
||
(rhs (nth b 2)))
|
||
(let ((tv (hk-fresh)))
|
||
(let ((r (hk-w cur-env rhs)))
|
||
(let ((s1 (first r)) (t1 (nth r 1)))
|
||
(let ((s2 (hk-unify (hk-subst-apply s1 tv) t1)))
|
||
(let ((s (hk-subst-compose s2 s1)))
|
||
(let ((t-gen (hk-generalize (hk-env-apply-subst s cur-env)
|
||
(hk-subst-apply s t1))))
|
||
(let ((bindings (hk-w-pat pat t-gen)))
|
||
(let ((r2 (hk-dict-copy cur-env)))
|
||
(for-each
|
||
(fn (k) (dict-set! r2 k (get bindings k)))
|
||
(keys bindings))
|
||
r2))))))))))
|
||
;; Function clause: let f x y = expr
|
||
((= tag "fun-clause")
|
||
(let ((name (nth b 1))
|
||
(pats (nth b 2))
|
||
(body2 (nth b 3)))
|
||
;; Treat as: let name = lambda pats body2
|
||
(let ((rhs (if (empty? pats)
|
||
body2
|
||
(list "lambda" pats body2))))
|
||
(let ((tv (hk-fresh)))
|
||
(let ((env-rec (hk-dict-copy cur-env)))
|
||
(dict-set! env-rec name tv)
|
||
(let ((r (hk-w env-rec rhs)))
|
||
(let ((s1 (first r)) (t1 (nth r 1)))
|
||
(let ((s2 (hk-unify (hk-subst-apply s1 tv) t1)))
|
||
(let ((s (hk-subst-compose s2 s1)))
|
||
(let ((t-gen (hk-generalize
|
||
(hk-env-apply-subst s cur-env)
|
||
(hk-subst-apply s t1))))
|
||
(let ((r2 (hk-dict-copy cur-env)))
|
||
(dict-set! r2 name t-gen)
|
||
r2)))))))))))
|
||
(:else cur-env))))
|
||
env
|
||
binds)))
|
||
(hk-w env2 body))))
|
||
|
||
(define
|
||
hk-w
|
||
(fn
|
||
(env expr)
|
||
(let ((tag (first expr)))
|
||
(cond
|
||
;; Literals
|
||
((= tag "int") (list hk-subst-empty hk-t-int))
|
||
((= tag "float") (list hk-subst-empty hk-t-float))
|
||
((= tag "string") (list hk-subst-empty hk-t-string))
|
||
((= tag "char") (list hk-subst-empty hk-t-char))
|
||
|
||
;; Variable
|
||
((= tag "var")
|
||
(let ((name (nth expr 1)))
|
||
(let ((scheme (get env name)))
|
||
(if (nil? scheme)
|
||
(raise (str "Unbound variable: " name))
|
||
(list hk-subst-empty (hk-instantiate scheme))))))
|
||
|
||
;; Constructor (same lookup as var)
|
||
((= tag "con")
|
||
(let ((name (nth expr 1)))
|
||
(let ((scheme (get env name)))
|
||
(if (nil? scheme)
|
||
(list hk-subst-empty (hk-fresh))
|
||
(list hk-subst-empty (hk-instantiate scheme))))))
|
||
|
||
;; Unary negation
|
||
((= tag "neg")
|
||
(let ((r (hk-w env (nth expr 1))))
|
||
(let ((s1 (first r)) (t1 (nth r 1)))
|
||
(let ((s2 (hk-unify t1 hk-t-int)))
|
||
(list (hk-subst-compose s2 s1) hk-t-int)))))
|
||
|
||
;; Lambda: ("lambda" pats body)
|
||
((= tag "lambda")
|
||
(let ((pats (nth expr 1))
|
||
(body (nth expr 2)))
|
||
(if (empty? pats)
|
||
(hk-w env body)
|
||
(let ((pat (first pats))
|
||
(rest (rest pats)))
|
||
(let ((tv (hk-fresh)))
|
||
(let ((bindings (hk-w-pat pat tv)))
|
||
(let ((env2 (hk-dict-copy env)))
|
||
(for-each (fn (k) (dict-set! env2 k (get bindings k))) (keys bindings))
|
||
(let ((inner (if (empty? rest)
|
||
body
|
||
(list "lambda" rest body))))
|
||
(let ((r (hk-w env2 inner)))
|
||
(let ((s1 (first r)) (t1 (nth r 1)))
|
||
(list s1 (hk-tarr (hk-subst-apply s1 tv) t1))))))))))))
|
||
|
||
;; Application: ("app" f x)
|
||
((= tag "app")
|
||
(let ((tv (hk-fresh)))
|
||
(let ((r1 (hk-w env (nth expr 1))))
|
||
(let ((s1 (first r1)) (tf (nth r1 1)))
|
||
(let ((r2 (hk-w (hk-env-apply-subst s1 env) (nth expr 2))))
|
||
(let ((s2 (first r2)) (tx (nth r2 1)))
|
||
(let ((s3 (hk-unify (hk-subst-apply s2 tf) (hk-tarr tx tv))))
|
||
(let ((s (hk-subst-compose s3 (hk-subst-compose s2 s1))))
|
||
(list s (hk-subst-apply s3 tv))))))))))
|
||
|
||
;; Let: ("let" binds body)
|
||
((= tag "let")
|
||
(hk-w-let env (nth expr 1) (nth expr 2)))
|
||
|
||
;; If: ("if" cond then else)
|
||
((= tag "if")
|
||
(let ((r1 (hk-w env (nth expr 1))))
|
||
(let ((s1 (first r1)) (tc (nth r1 1)))
|
||
(let ((s2 (hk-unify tc hk-t-bool)))
|
||
(let ((s12 (hk-subst-compose s2 s1)))
|
||
(let ((r2 (hk-w (hk-env-apply-subst s12 env) (nth expr 2))))
|
||
(let ((s3 (first r2)) (tt (nth r2 1)))
|
||
(let ((s123 (hk-subst-compose s3 s12)))
|
||
(let ((r3 (hk-w (hk-env-apply-subst s123 env) (nth expr 3))))
|
||
(let ((s4 (first r3)) (te (nth r3 1)))
|
||
(let ((s5 (hk-unify (hk-subst-apply s4 tt) te)))
|
||
(let ((s (hk-subst-compose s5 (hk-subst-compose s4 s123))))
|
||
(list s (hk-subst-apply s5 te))))))))))))))
|
||
|
||
;; Binary operator: ("op" op-name left right)
|
||
;; Desugar to double application.
|
||
((= tag "op")
|
||
(hk-w env
|
||
(list "app"
|
||
(list "app" (list "var" (nth expr 1)) (nth expr 2))
|
||
(nth expr 3))))
|
||
|
||
;; Tuple: ("tuple" [e1 e2 ...])
|
||
((= tag "tuple")
|
||
(let ((elems (nth expr 1)))
|
||
(let ((s-acc hk-subst-empty)
|
||
(ts (list)))
|
||
(for-each
|
||
(fn (e)
|
||
(let ((r (hk-w (hk-env-apply-subst s-acc env) e)))
|
||
(set! s-acc (hk-subst-compose (first r) s-acc))
|
||
(set! ts (append ts (list (nth r 1))))))
|
||
elems)
|
||
(list s-acc (hk-ttuple (map (fn (t) (hk-subst-apply s-acc t)) ts))))))
|
||
|
||
;; List literal: ("list" [e1 e2 ...])
|
||
((= tag "list")
|
||
(let ((elems (nth expr 1)))
|
||
(if (empty? elems)
|
||
(list hk-subst-empty (hk-t-list (hk-fresh)))
|
||
(let ((tv (hk-fresh)))
|
||
(let ((s-acc hk-subst-empty))
|
||
(for-each
|
||
(fn (e)
|
||
(let ((r (hk-w (hk-env-apply-subst s-acc env) e)))
|
||
(let ((s2 (first r)) (te (nth r 1)))
|
||
(let ((s3 (hk-unify (hk-subst-apply s2 tv) te)))
|
||
(set! s-acc (hk-subst-compose s3 (hk-subst-compose s2 s-acc)))))))
|
||
elems)
|
||
(list s-acc (hk-t-list (hk-subst-apply s-acc tv))))))))
|
||
|
||
;; Location annotation: just delegate — position is for outer context.
|
||
((= tag "loc")
|
||
(hk-w env (nth expr 3)))
|
||
|
||
(:else
|
||
(raise (str "hk-w: unhandled tag: " tag)))))))
|
||
|
||
;; ─── Initial type environment ─────────────────────────────────────────────────
|
||
;; Monomorphic numeric ops (no Num typeclass yet — upgraded in Phase 5).
|
||
|
||
(define
|
||
hk-type-env0
|
||
(fn ()
|
||
(let ((env (dict)))
|
||
;; Integer arithmetic
|
||
(for-each
|
||
(fn (op)
|
||
(dict-set! env op (hk-tarr hk-t-int (hk-tarr hk-t-int hk-t-int))))
|
||
(list "+" "-" "*" "div" "mod" "quot" "rem"))
|
||
;; Integer comparison → Bool
|
||
(for-each
|
||
(fn (op)
|
||
(dict-set! env op (hk-tarr hk-t-int (hk-tarr hk-t-int hk-t-bool))))
|
||
(list "==" "/=" "<" "<=" ">" ">="))
|
||
;; Boolean operators
|
||
(dict-set! env "&&" (hk-tarr hk-t-bool (hk-tarr hk-t-bool hk-t-bool)))
|
||
(dict-set! env "||" (hk-tarr hk-t-bool (hk-tarr hk-t-bool hk-t-bool)))
|
||
(dict-set! env "not" (hk-tarr hk-t-bool hk-t-bool))
|
||
;; Constructors
|
||
(dict-set! env "True" hk-t-bool)
|
||
(dict-set! env "False" hk-t-bool)
|
||
;; Polymorphic list ops (using TScheme)
|
||
(let ((a (hk-tvar "a")))
|
||
(dict-set! env "head" (hk-tscheme (list "a") (hk-tarr (hk-t-list a) a)))
|
||
(dict-set! env "tail" (hk-tscheme (list "a") (hk-tarr (hk-t-list a) (hk-t-list a))))
|
||
(dict-set! env "null" (hk-tscheme (list "a") (hk-tarr (hk-t-list a) hk-t-bool)))
|
||
(dict-set! env "length" (hk-tscheme (list "a") (hk-tarr (hk-t-list a) hk-t-int)))
|
||
(dict-set! env "reverse" (hk-tscheme (list "a") (hk-tarr (hk-t-list a) (hk-t-list a))))
|
||
(dict-set! env ":"
|
||
(hk-tscheme (list "a") (hk-tarr a (hk-tarr (hk-t-list a) (hk-t-list a))))))
|
||
;; negate
|
||
(dict-set! env "negate" (hk-tarr hk-t-int hk-t-int))
|
||
(dict-set! env "abs" (hk-tarr hk-t-int hk-t-int))
|
||
env)))
|
||
|
||
;; ─── Expression brief printer ────────────────────────────────────────────────
|
||
;; Produces a short human-readable label for an AST node used in error messages.
|
||
|
||
(define
|
||
hk-expr->brief
|
||
(fn
|
||
(expr)
|
||
(cond
|
||
((not (list? expr)) (str expr))
|
||
((empty? expr) "()")
|
||
(:else
|
||
(let ((tag (first expr)))
|
||
(cond
|
||
((= tag "var") (nth expr 1))
|
||
((= tag "con") (nth expr 1))
|
||
((= tag "int") (str (nth expr 1)))
|
||
((= tag "float") (str (nth expr 1)))
|
||
((= tag "string") (str "\"" (nth expr 1) "\""))
|
||
((= tag "char") (str "'" (nth expr 1) "'"))
|
||
((= tag "neg") (str "(-" (hk-expr->brief (nth expr 1)) ")"))
|
||
((= tag "app")
|
||
(str "(" (hk-expr->brief (nth expr 1))
|
||
" " (hk-expr->brief (nth expr 2)) ")"))
|
||
((= tag "op")
|
||
(str "(" (hk-expr->brief (nth expr 2))
|
||
" " (nth expr 1)
|
||
" " (hk-expr->brief (nth expr 3)) ")"))
|
||
((= tag "lambda") "(\\ ...)")
|
||
((= tag "let") "(let ...)")
|
||
((= tag "if") "(if ...)")
|
||
((= tag "tuple") "(tuple ...)")
|
||
((= tag "list") "[...]")
|
||
((= tag "loc") (hk-expr->brief (nth expr 3)))
|
||
(:else (str "(" tag " ..."))))))))
|
||
|
||
;; ─── Loc-annotated inference ──────────────────────────────────────────────────
|
||
;; ("loc" LINE COL INNER) node: hk-w catches any error and re-raises with
|
||
;; "at LINE:COL: " prepended. Emitted by the parser or test scaffolding.
|
||
|
||
;; Extended hk-w handles "loc" — handled inline in the cond below.
|
||
|
||
;; ─── Program-level inference ─────────────────────────────────────────────────
|
||
;; hk-infer-decl : env × decl → ("ok" name type-str) | ("err" msg) | nil
|
||
;; Uses tagged results so callers don't need re-raise.
|
||
|
||
(define
|
||
hk-infer-decl
|
||
(fn
|
||
(env decl)
|
||
(let
|
||
((tag (first decl)))
|
||
(cond
|
||
((= tag "fun-clause")
|
||
(let
|
||
((name (nth decl 1)) (pats (nth decl 2)) (body (nth decl 3)))
|
||
(let
|
||
((rhs (if (empty? pats) body (list "lambda" pats body))))
|
||
(guard
|
||
(e (#t (list "err" (str "in '" name "': " e))))
|
||
(begin
|
||
(hk-reset-fresh)
|
||
(let
|
||
((r (hk-w env rhs)))
|
||
(let
|
||
((final-type (hk-subst-apply (first r) (nth r 1))))
|
||
(list "ok" name (hk-type->str final-type) final-type))))))))
|
||
((or (= tag "bind") (= tag "pat-bind"))
|
||
(let
|
||
((pat (nth decl 1)) (body (nth decl 2)))
|
||
(let
|
||
((label (if (and (list? pat) (= (first pat) "p-var")) (nth pat 1) "<binding>")))
|
||
(guard
|
||
(e (#t (list "err" (str "in '" label "': " e))))
|
||
(begin
|
||
(hk-reset-fresh)
|
||
(let
|
||
((r (hk-w env body)))
|
||
(let
|
||
((final-type (hk-subst-apply (first r) (nth r 1))))
|
||
(list "ok" label (hk-type->str final-type) final-type))))))))
|
||
(:else nil)))))
|
||
|
||
;; hk-infer-prog : program-ast × env → list of ("ok" name type) | ("err" msg)
|
||
|
||
(define
|
||
hk-infer-prog
|
||
(fn
|
||
(prog env)
|
||
(let
|
||
((decls (cond ((and (list? prog) (= (first prog) "program")) (nth prog 1)) ((and (list? prog) (= (first prog) "module")) (nth prog 3)) (:else (list))))
|
||
(results (list)))
|
||
(for-each
|
||
(fn
|
||
(d)
|
||
(let
|
||
((r (hk-infer-decl env d)))
|
||
(when
|
||
(not (nil? r))
|
||
(append! results r)
|
||
(when
|
||
(= (first r) "ok")
|
||
(dict-set! env (nth r 1) (nth r 3))))))
|
||
decls)
|
||
results)))
|
||
|
||
;; ─── Convenience ─────────────────────────────────────────────────────────────
|
||
;; hk-infer-type : Haskell expression source → inferred type string
|
||
|
||
(define
|
||
hk-infer-type
|
||
(fn
|
||
(src)
|
||
(hk-reset-fresh)
|
||
(let ((ast (hk-core-expr src))
|
||
(env (hk-type-env0)))
|
||
(let ((r (hk-w env ast)))
|
||
(hk-type->str (hk-subst-apply (first r) (nth r 1)))))))
|