Iter 16b.6: LetRec inside polymorphic enclosing fn
Lifts the monomorphic-only restriction. Lifted top-level fn now
becomes Forall(α..., Fn(t..., T...) -> tr) when the enclosing fn
is polymorphic; capture types may mention outer type vars and
the existing mono pipeline (Iter 12b/14a) specializes them
correctly at call sites.
- desugar.rs / lift.rs: scope-building unified — Forall fn-params
are now KnownType, not LetBound. Lift wraps augmented_ty in
Forall mirroring the enclosing fn.
- Name-as-value-in-in-term in a polymorphic enclosing fn is
rejected (eta-Lam wrap from 16b.5 has no Forall). Queued
separately as closure-conversion-with-polymorphism.
- examples/poly_rec_capture.{ailx,ail.json}: apply_n_times :
Forall(a). drives at Int and Bool to exercise two mono
instantiations.
- e2e + check + desugar tests: 116 → 120 (+4).
Codegen pipeline untouched — apply_subst_to_type substitutes
through Fn.params uniformly (original params + appended captures).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"defs":[{"body":{"args":[{"name":"x","t":"var"},{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"+","t":"var"},"t":"app"},"doc":"Helper: increment Int. Used as the Fn(Int) -> Int instance.","kind":"fn","name":"succ","params":["x"],"type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Int"}}},{"body":{"args":[{"name":"b","t":"var"}],"fn":{"name":"not","t":"var"},"t":"app"},"doc":"Helper: boolean negation as a top-level fn (so it has an adapter for value-position).","kind":"fn","name":"flip","params":["b"],"type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Bool"}],"ret":{"k":"con","name":"Bool"}}},{"body":{"body":{"cond":{"args":[{"name":"k","t":"var"},{"lit":{"kind":"int","value":0},"t":"lit"}],"fn":{"name":"==","t":"var"},"t":"app"},"else":{"args":[{"args":[{"name":"k","t":"var"},{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"-","t":"var"},"t":"app"},{"args":[{"name":"acc","t":"var"}],"fn":{"name":"f","t":"var"},"t":"app"}],"fn":{"name":"loop","t":"var"},"t":"app"},"t":"if","then":{"name":"acc","t":"var"}},"in":{"args":[{"name":"n","t":"var"},{"name":"x","t":"var"}],"fn":{"name":"loop","t":"var"},"t":"app"},"name":"loop","params":["k","acc"],"t":"letrec","type":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"},{"k":"var","name":"a"}],"ret":{"k":"var","name":"a"}}},"doc":"Apply f to x exactly n times. Polymorphic in a.","kind":"fn","name":"apply_n_times","params":["n","x","f"],"type":{"body":{"effects":[],"k":"fn","params":[{"k":"con","name":"Int"},{"k":"var","name":"a"},{"effects":[],"k":"fn","params":[{"k":"var","name":"a"}],"ret":{"k":"var","name":"a"}}],"ret":{"k":"var","name":"a"}},"k":"forall","vars":["a"]}},{"body":{"lhs":{"args":[{"args":[{"lit":{"kind":"int","value":5},"t":"lit"},{"lit":{"kind":"int","value":0},"t":"lit"},{"name":"succ","t":"var"}],"fn":{"name":"apply_n_times","t":"var"},"t":"app"}],"op":"io/print_int","t":"do"},"rhs":{"args":[{"args":[{"lit":{"kind":"int","value":4},"t":"lit"},{"lit":{"kind":"bool","value":false},"t":"lit"},{"name":"flip","t":"var"}],"fn":{"name":"apply_n_times","t":"var"},"t":"app"}],"op":"io/print_bool","t":"do"},"t":"seq"},"doc":"Drive apply_n_times at Int (succ 5 times from 0) and Bool (not 4 times from false).","kind":"fn","name":"main","params":[],"type":{"effects":["IO"],"k":"fn","params":[],"ret":{"k":"con","name":"Unit"}}}],"imports":[],"name":"poly_rec_capture","schema":"ailang/v0"}
|
||||
@@ -0,0 +1,57 @@
|
||||
; Iter 16b.6 — LetRec inside a polymorphic enclosing fn.
|
||||
; `apply_n_times : Forall(a). Fn(Int, a, Fn(a) -> a) -> a`
|
||||
; applies `f` to `x` exactly `n` times. The recursive helper `loop`
|
||||
; captures `f` from the enclosing fn's params — `f`'s declared type
|
||||
; is `Fn(a) -> a`, which mentions the outer fn's type var `a`. The
|
||||
; 16b.6 lift produces a synthetic top-level fn
|
||||
; `loop$lr_0 : Forall(a). Fn(Int, a, Fn(a) -> a) -> a`
|
||||
; and rewrites `(app loop k acc)` → `(app loop$lr_0 k acc f)`.
|
||||
;
|
||||
; `main` drives `apply_n_times` at TWO distinct type instantiations
|
||||
; so codegen's monomorphisation queue picks up `loop$lr_0` twice
|
||||
; (once at `a = Int` to compute `succ` applied 5x to 0 → 5; once at
|
||||
; `a = Bool` to compute `not` applied 4x to false → false). This
|
||||
; exercises the substitution of the outer Forall.vars into both the
|
||||
; original LetRec params and the appended capture types.
|
||||
;
|
||||
; Expected stdout (one per line): 5, false.
|
||||
|
||||
(module poly_rec_capture
|
||||
|
||||
(fn succ
|
||||
(doc "Helper: increment Int. Used as the Fn(Int) -> Int instance.")
|
||||
(type (fn-type (params (con Int)) (ret (con Int))))
|
||||
(params x)
|
||||
(body (app + x 1)))
|
||||
|
||||
(fn flip
|
||||
(doc "Helper: boolean negation as a top-level fn (so it has an adapter for value-position).")
|
||||
(type (fn-type (params (con Bool)) (ret (con Bool))))
|
||||
(params b)
|
||||
(body (app not b)))
|
||||
|
||||
(fn apply_n_times
|
||||
(doc "Apply f to x exactly n times. Polymorphic in a.")
|
||||
(type
|
||||
(forall (vars a)
|
||||
(fn-type
|
||||
(params (con Int) a (fn-type (params a) (ret a)))
|
||||
(ret a))))
|
||||
(params n x f)
|
||||
(body
|
||||
(let-rec loop
|
||||
(params k acc)
|
||||
(type (fn-type (params (con Int) a) (ret a)))
|
||||
(body
|
||||
(if (app == k 0)
|
||||
acc
|
||||
(app loop (app - k 1) (app f acc))))
|
||||
(in (app loop n x)))))
|
||||
|
||||
(fn main
|
||||
(doc "Drive apply_n_times at Int (succ 5 times from 0) and Bool (not 4 times from false).")
|
||||
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
||||
(params)
|
||||
(body
|
||||
(seq (do io/print_int (app apply_n_times 5 0 succ))
|
||||
(do io/print_bool (app apply_n_times 4 false flip))))))
|
||||
Reference in New Issue
Block a user