@@ -27,11 +27,22 @@ use std::collections::HashMap;
2727// Type Environment
2828// ============================================
2929
30+ /// A (possibly polymorphic) type scheme: `∀ vars. ty`. An empty `vars` is a
31+ /// monomorphic type. Used for let-generalization — each use site of a
32+ /// polymorphic binding instantiates the quantified variables afresh.
33+ #[ derive( Debug , Clone ) ]
34+ pub struct Scheme {
35+ /// Quantified type-variable ids.
36+ pub vars : Vec < u32 > ,
37+ /// The body type (may mention `vars` and/or monomorphic free vars).
38+ pub ty : Type ,
39+ }
40+
3041/// Type environment with scoping and type variable generation.
3142#[ derive( Debug , Clone ) ]
3243pub struct CheckEnv {
33- /// Variable-to-type bindings in current scope.
34- bindings : HashMap < String , Type > ,
44+ /// Variable-to-scheme bindings in current scope.
45+ bindings : HashMap < String , Scheme > ,
3546 /// Parent scope (for lexical scoping).
3647 parent : Option < Box < CheckEnv > > ,
3748 /// Counter for generating fresh type variables.
@@ -67,18 +78,132 @@ impl CheckEnv {
6778 }
6879 }
6980
70- /// Bind a name to a type in the current scope.
81+ /// Bind a name to a *monomorphic* type in the current scope.
7182 pub fn bind ( & mut self , name : String , ty : Type ) {
72- self . bindings . insert ( name, ty) ;
83+ self . bindings . insert ( name, Scheme { vars : Vec :: new ( ) , ty } ) ;
84+ }
85+
86+ /// Bind a name to a (possibly polymorphic) type scheme.
87+ pub fn bind_scheme ( & mut self , name : String , scheme : Scheme ) {
88+ self . bindings . insert ( name, scheme) ;
7389 }
7490
75- /// Look up a name, searching parent scopes.
76- pub fn lookup ( & self , name : & str ) -> Option < & Type > {
91+ /// Look up a name's scheme , searching parent scopes.
92+ pub fn lookup ( & self , name : & str ) -> Option < & Scheme > {
7793 self . bindings
7894 . get ( name)
7995 . or_else ( || self . parent . as_ref ( ) . and_then ( |p| p. lookup ( name) ) )
8096 }
8197
98+ /// Instantiate a scheme: replace each quantified variable with a fresh one,
99+ /// so every use site of a polymorphic binding is independent.
100+ pub fn instantiate ( & mut self , scheme : & Scheme ) -> Type {
101+ if scheme. vars . is_empty ( ) {
102+ return scheme. ty . clone ( ) ;
103+ }
104+ let mut mapping = HashMap :: new ( ) ;
105+ for & v in & scheme. vars {
106+ let fresh = self . fresh_var ( ) ;
107+ mapping. insert ( v, fresh) ;
108+ }
109+ Self :: subst_vars ( & scheme. ty , & mapping)
110+ }
111+
112+ /// Generalize a type into a scheme by quantifying over the type variables
113+ /// free in it but not free in the environment (HM let-generalization).
114+ pub fn generalize ( & self , ty : & Type ) -> Scheme {
115+ let resolved = self . resolve ( ty) ;
116+ let mut ty_vars = std:: collections:: BTreeSet :: new ( ) ;
117+ Self :: collect_vars ( & resolved, & mut ty_vars) ;
118+ let env_vars = self . env_free_vars ( ) ;
119+ let vars: Vec < u32 > = ty_vars
120+ . into_iter ( )
121+ . filter ( |v| !env_vars. contains ( v) )
122+ . collect ( ) ;
123+ Scheme { vars, ty : resolved }
124+ }
125+
126+ /// Substitute the mapped type variables throughout a type.
127+ fn subst_vars ( ty : & Type , m : & HashMap < u32 , Type > ) -> Type {
128+ match ty {
129+ Type :: Var ( id) => m. get ( id) . cloned ( ) . unwrap_or_else ( || ty. clone ( ) ) ,
130+ Type :: Fun ( a, b) => Type :: Fun (
131+ Box :: new ( Self :: subst_vars ( a, m) ) ,
132+ Box :: new ( Self :: subst_vars ( b, m) ) ,
133+ ) ,
134+ Type :: Dist ( t) => Type :: Dist ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
135+ Type :: List ( t) => Type :: List ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
136+ Type :: Set ( t) => Type :: Set ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
137+ Type :: Option ( t) => Type :: Option ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
138+ Type :: Echo ( t) => Type :: Echo ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
139+ Type :: EchoR ( t) => Type :: EchoR ( Box :: new ( Self :: subst_vars ( t, m) ) ) ,
140+ Type :: Map ( k, v) => Type :: Map (
141+ Box :: new ( Self :: subst_vars ( k, m) ) ,
142+ Box :: new ( Self :: subst_vars ( v, m) ) ,
143+ ) ,
144+ Type :: Result ( a, b) => Type :: Result (
145+ Box :: new ( Self :: subst_vars ( a, m) ) ,
146+ Box :: new ( Self :: subst_vars ( b, m) ) ,
147+ ) ,
148+ Type :: Tuple ( es) => Type :: Tuple ( es. iter ( ) . map ( |e| Self :: subst_vars ( e, m) ) . collect ( ) ) ,
149+ _ => ty. clone ( ) ,
150+ }
151+ }
152+
153+ /// Collect the type variables appearing in a type.
154+ fn collect_vars ( ty : & Type , out : & mut std:: collections:: BTreeSet < u32 > ) {
155+ match ty {
156+ Type :: Var ( id) => {
157+ out. insert ( * id) ;
158+ }
159+ Type :: Fun ( a, b) => {
160+ Self :: collect_vars ( a, out) ;
161+ Self :: collect_vars ( b, out) ;
162+ }
163+ Type :: Dist ( t)
164+ | Type :: List ( t)
165+ | Type :: Set ( t)
166+ | Type :: Option ( t)
167+ | Type :: Echo ( t)
168+ | Type :: EchoR ( t) => Self :: collect_vars ( t, out) ,
169+ Type :: Map ( k, v) => {
170+ Self :: collect_vars ( k, out) ;
171+ Self :: collect_vars ( v, out) ;
172+ }
173+ Type :: Result ( a, b) => {
174+ Self :: collect_vars ( a, out) ;
175+ Self :: collect_vars ( b, out) ;
176+ }
177+ Type :: Tuple ( es) => {
178+ for e in es {
179+ Self :: collect_vars ( e, out) ;
180+ }
181+ }
182+ _ => { }
183+ }
184+ }
185+
186+ /// Type variables free in the environment (this scope and parents),
187+ /// excluding each binding's own quantified variables.
188+ fn env_free_vars ( & self ) -> std:: collections:: BTreeSet < u32 > {
189+ let mut out = std:: collections:: BTreeSet :: new ( ) ;
190+ let mut cur = Some ( self ) ;
191+ while let Some ( e) = cur {
192+ for scheme in e. bindings . values ( ) {
193+ let resolved = self . resolve ( & scheme. ty ) ;
194+ let mut vs = std:: collections:: BTreeSet :: new ( ) ;
195+ Self :: collect_vars ( & resolved, & mut vs) ;
196+ for v in vs {
197+ if !scheme. vars . contains ( & v) {
198+ out. insert ( v) ;
199+ }
200+ }
201+ }
202+ cur = e. parent . as_deref ( ) ;
203+ }
204+ out
205+ }
206+
82207 /// Generate a fresh type variable.
83208 pub fn fresh_var ( & mut self ) -> Type {
84209 let id = self . next_var ;
@@ -245,11 +370,18 @@ fn seed_builtins(env: &mut CheckEnv) {
245370 "print" . to_string ( ) ,
246371 Type :: Fun ( Box :: new ( Type :: String ) , Box :: new ( Type :: Unit ) ) ,
247372 ) ;
248- // to_string : 'a -> String (polymorphic, approximated with a var)
249- let a = env. fresh_var ( ) ;
250- env. bind (
373+ // to_string : ∀a. a -> String (properly polymorphic via a type scheme,
374+ // so each use site instantiates a fresh carrier instead of sharing one var).
375+ let a_id = match env. fresh_var ( ) {
376+ Type :: Var ( id) => id,
377+ _ => unreachable ! ( "fresh_var always returns Type::Var" ) ,
378+ } ;
379+ env. bind_scheme (
251380 "to_string" . to_string ( ) ,
252- Type :: Fun ( Box :: new ( a) , Box :: new ( Type :: String ) ) ,
381+ Scheme {
382+ vars : vec ! [ a_id] ,
383+ ty : Type :: Fun ( Box :: new ( Type :: Var ( a_id) ) , Box :: new ( Type :: String ) ) ,
384+ } ,
253385 ) ;
254386}
255387
@@ -356,7 +488,10 @@ fn check_item(item: &Item, env: &mut CheckEnv) -> CompileResult<()> {
356488 Item :: Let ( def) => {
357489 let ty = check_let_def ( def, env) ?;
358490 let name = def. name . node . to_string ( ) ;
359- env. bind ( name, ty) ;
491+ // Let-generalization: a top-level binding is polymorphic over the
492+ // type variables free in its type but not free in the environment.
493+ let scheme = env. generalize ( & ty) ;
494+ env. bind_scheme ( name, scheme) ;
360495 Ok ( ( ) )
361496 }
362497 Item :: TypeDef ( _) => {
@@ -414,8 +549,8 @@ fn check_let_def(def: &LetDef, env: &mut CheckEnv) -> CompileResult<Type> {
414549
415550 // For recursive defs, unify the placeholder with the result.
416551 if def. is_rec {
417- if let Some ( placeholder ) = inner_env. lookup ( & def. name . node . to_string ( ) ) {
418- let placeholder = placeholder . clone ( ) ;
552+ if let Some ( scheme ) = inner_env. lookup ( & def. name . node . to_string ( ) ) {
553+ let placeholder = scheme . ty . clone ( ) ;
419554 inner_env. unify ( & placeholder, & result, Some ( def. name . span ) ) ?;
420555 }
421556 }
@@ -457,8 +592,8 @@ fn check_expr(expr: &Spanned<Expr>, env: &mut CheckEnv) -> CompileResult<Type> {
457592 // shadowed by a user binding of the same name); otherwise fall back
458593 // to the polymorphic echo-type operations, which are freshly
459594 // instantiated per use site.
460- if let Some ( ty ) = env. lookup ( & n) . cloned ( ) {
461- Ok ( ty )
595+ if let Some ( scheme ) = env. lookup ( & n) . cloned ( ) {
596+ Ok ( env . instantiate ( & scheme ) )
462597 } else if let Some ( ty) = echo_builtin_type ( & n, env) {
463598 Ok ( ty)
464599 } else {
@@ -1621,4 +1756,47 @@ mod tests {
16211756 env. unify ( & a, & Type :: Fun ( Box :: new ( b) , Box :: new ( Type :: Int ) ) , None )
16221757 . expect ( "'a = ('b -> Int) is a finite type" ) ;
16231758 }
1759+
1760+ // ---- Type schemes / let-polymorphism (generalize + instantiate) ----
1761+
1762+ /// A generalized scheme instantiates independently at each use, so the same
1763+ /// polymorphic value can be applied at two different types in one scope.
1764+ #[ test]
1765+ fn test_generalize_instantiate_independent ( ) {
1766+ let mut env = CheckEnv :: new ( ) ;
1767+ let a = env. fresh_var ( ) ;
1768+ let id_ty = Type :: Fun ( Box :: new ( a. clone ( ) ) , Box :: new ( a) ) ;
1769+ let scheme = env. generalize ( & id_ty) ;
1770+ assert_eq ! ( scheme. vars. len( ) , 1 , "the free var should be generalized" ) ;
1771+ let i1 = env. instantiate ( & scheme) ;
1772+ let i2 = env. instantiate ( & scheme) ;
1773+ env. unify ( & i1, & Type :: Fun ( Box :: new ( Type :: Int ) , Box :: new ( Type :: Int ) ) , None )
1774+ . expect ( "first instance specialises to Int -> Int" ) ;
1775+ env. unify (
1776+ & i2,
1777+ & Type :: Fun ( Box :: new ( Type :: String ) , Box :: new ( Type :: String ) ) ,
1778+ None ,
1779+ )
1780+ . expect ( "second instance specialises to String -> String independently" ) ;
1781+ }
1782+
1783+ /// The seeded `to_string : ∀a. a -> String` is genuinely polymorphic: it
1784+ /// type-checks at `Int` and at `String` in the same scope. (The old
1785+ /// shared-variable approximation pinned it to the first use and would have
1786+ /// rejected the second.)
1787+ #[ test]
1788+ fn test_to_string_is_polymorphic_across_uses ( ) {
1789+ let mut env = CheckEnv :: new ( ) ;
1790+ seed_builtins ( & mut env) ;
1791+ let at_int = call ( "to_string" , vec ! [ dummy( Expr :: Int ( 1 ) ) ] ) ;
1792+ assert_eq ! (
1793+ check_expr( & at_int, & mut env) . expect( "to_string 1 : String" ) ,
1794+ Type :: String
1795+ ) ;
1796+ let at_str = call ( "to_string" , vec ! [ dummy( Expr :: String ( "x" . into( ) ) ) ] ) ;
1797+ assert_eq ! (
1798+ check_expr( & at_str, & mut env) . expect( "to_string \" x\" : String" ) ,
1799+ Type :: String
1800+ ) ;
1801+ }
16241802}
0 commit comments