/home/noah/src/ruchy/src/middleend/types.rs
Line | Count | Source |
1 | | //! Type system representation for Ruchy |
2 | | |
3 | | use std::collections::HashMap; |
4 | | use std::fmt; |
5 | | |
6 | | /// Type variable for unification |
7 | | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
8 | | pub struct TyVar(pub u32); |
9 | | |
10 | | impl fmt::Display for TyVar { |
11 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
12 | 0 | write!(f, "τ{}", self.0) |
13 | 0 | } |
14 | | } |
15 | | |
16 | | /// Monomorphic types in the Hindley-Milner system |
17 | | #[derive(Debug, Clone, PartialEq)] |
18 | | pub enum MonoType { |
19 | | /// Type variable (unknown type to be inferred) |
20 | | Var(TyVar), |
21 | | /// Primitive integer type |
22 | | Int, |
23 | | /// Primitive float type |
24 | | Float, |
25 | | /// Primitive boolean type |
26 | | Bool, |
27 | | /// String type |
28 | | String, |
29 | | /// Character type |
30 | | Char, |
31 | | /// Unit type () |
32 | | Unit, |
33 | | /// Function type: T1 -> T2 |
34 | | Function(Box<MonoType>, Box<MonoType>), |
35 | | /// List type: `[T]` |
36 | | List(Box<MonoType>), |
37 | | /// Tuple type: (T1, T2, ...) |
38 | | Tuple(Vec<MonoType>), |
39 | | /// Optional type: T? |
40 | | Optional(Box<MonoType>), |
41 | | /// Result type: Result<T, E> |
42 | | Result(Box<MonoType>, Box<MonoType>), |
43 | | /// Named type (user-defined or gradual typing 'Any') |
44 | | Named(String), |
45 | | /// Reference type: &T |
46 | | Reference(Box<MonoType>), |
47 | | /// `DataFrame` type with column names and their types |
48 | | DataFrame(Vec<(String, MonoType)>), |
49 | | /// Series type with element type |
50 | | Series(Box<MonoType>), |
51 | | } |
52 | | |
53 | | impl fmt::Display for MonoType { |
54 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
55 | 0 | match self { |
56 | 0 | MonoType::Var(v) => write!(f, "{v}"), |
57 | 0 | MonoType::Int => write!(f, "i32"), |
58 | 0 | MonoType::Float => write!(f, "f64"), |
59 | 0 | MonoType::Bool => write!(f, "bool"), |
60 | 0 | MonoType::String => write!(f, "String"), |
61 | 0 | MonoType::Char => write!(f, "char"), |
62 | 0 | MonoType::Unit => write!(f, "()"), |
63 | 0 | MonoType::Function(arg, ret) => write!(f, "({arg} -> {ret})"), |
64 | 0 | MonoType::List(elem) => write!(f, "[{elem}]"), |
65 | 0 | MonoType::Optional(inner) => write!(f, "{inner}?"), |
66 | 0 | MonoType::Result(ok, err) => write!(f, "Result<{ok}, {err}>"), |
67 | 0 | MonoType::Tuple(types) => { |
68 | 0 | write!(f, "(")?; |
69 | 0 | for (i, ty) in types.iter().enumerate() { |
70 | 0 | if i > 0 { |
71 | 0 | write!(f, ", ")?; |
72 | 0 | } |
73 | 0 | write!(f, "{ty}")?; |
74 | | } |
75 | 0 | write!(f, ")") |
76 | | } |
77 | 0 | MonoType::Named(name) => write!(f, "{name}"), |
78 | 0 | MonoType::Reference(inner) => write!(f, "&{inner}"), |
79 | 0 | MonoType::DataFrame(columns) => { |
80 | 0 | write!(f, "DataFrame[")?; |
81 | 0 | for (i, (name, ty)) in columns.iter().enumerate() { |
82 | 0 | if i > 0 { |
83 | 0 | write!(f, ", ")?; |
84 | 0 | } |
85 | 0 | write!(f, "{name}: {ty}")?; |
86 | | } |
87 | 0 | write!(f, "]") |
88 | | } |
89 | 0 | MonoType::Series(dtype) => write!(f, "Series<{dtype}>"), |
90 | | } |
91 | 0 | } |
92 | | } |
93 | | |
94 | | /// Polymorphic type scheme: ∀α₁...αₙ. τ |
95 | | #[derive(Debug, Clone)] |
96 | | pub struct TypeScheme { |
97 | | /// Quantified type variables |
98 | | pub vars: Vec<TyVar>, |
99 | | /// The monomorphic type |
100 | | pub ty: MonoType, |
101 | | } |
102 | | |
103 | | impl TypeScheme { |
104 | | /// Create a monomorphic type scheme (no quantified variables) |
105 | | #[must_use] |
106 | 0 | pub fn mono(ty: MonoType) -> Self { |
107 | 0 | TypeScheme { |
108 | 0 | vars: Vec::new(), |
109 | 0 | ty, |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | | /// Instantiate a type scheme with fresh type variables |
114 | 0 | pub fn instantiate(&self, gen: &mut TyVarGenerator) -> MonoType { |
115 | 0 | if self.vars.is_empty() { |
116 | 0 | self.ty.clone() |
117 | | } else { |
118 | 0 | let subst: HashMap<TyVar, MonoType> = self |
119 | 0 | .vars |
120 | 0 | .iter() |
121 | 0 | .map(|v| (v.clone(), MonoType::Var(gen.fresh()))) |
122 | 0 | .collect(); |
123 | 0 | self.ty.substitute(&subst) |
124 | | } |
125 | 0 | } |
126 | | } |
127 | | |
128 | | impl fmt::Display for TypeScheme { |
129 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
130 | 0 | if self.vars.is_empty() { |
131 | 0 | write!(f, "{}", self.ty) |
132 | | } else { |
133 | 0 | write!(f, "∀")?; |
134 | 0 | for (i, var) in self.vars.iter().enumerate() { |
135 | 0 | if i > 0 { |
136 | 0 | write!(f, ",")?; |
137 | 0 | } |
138 | 0 | write!(f, "{var}")?; |
139 | | } |
140 | 0 | write!(f, ". {}", self.ty) |
141 | | } |
142 | 0 | } |
143 | | } |
144 | | |
145 | | /// Type variable generator for fresh variables |
146 | | pub struct TyVarGenerator { |
147 | | next: u32, |
148 | | } |
149 | | |
150 | | impl TyVarGenerator { |
151 | | #[must_use] |
152 | 0 | pub fn new() -> Self { |
153 | 0 | TyVarGenerator { next: 0 } |
154 | 0 | } |
155 | | |
156 | 0 | pub fn fresh(&mut self) -> TyVar { |
157 | 0 | let var = TyVar(self.next); |
158 | 0 | self.next += 1; |
159 | 0 | var |
160 | 0 | } |
161 | | } |
162 | | |
163 | | impl Default for TyVarGenerator { |
164 | 0 | fn default() -> Self { |
165 | 0 | Self::new() |
166 | 0 | } |
167 | | } |
168 | | |
169 | | /// Substitution mapping from type variables to types |
170 | | pub type Substitution = HashMap<TyVar, MonoType>; |
171 | | |
172 | | impl MonoType { |
173 | | /// Apply a substitution to this type |
174 | | #[must_use] |
175 | 0 | pub fn substitute(&self, subst: &Substitution) -> MonoType { |
176 | 0 | match self { |
177 | 0 | MonoType::Var(v) => subst.get(v).cloned().unwrap_or_else(|| self.clone()), |
178 | 0 | MonoType::Function(arg, ret) => MonoType::Function( |
179 | 0 | Box::new(arg.substitute(subst)), |
180 | 0 | Box::new(ret.substitute(subst)), |
181 | 0 | ), |
182 | 0 | MonoType::List(elem) => MonoType::List(Box::new(elem.substitute(subst))), |
183 | 0 | MonoType::Optional(inner) => MonoType::Optional(Box::new(inner.substitute(subst))), |
184 | 0 | MonoType::Result(ok, err) => MonoType::Result( |
185 | 0 | Box::new(ok.substitute(subst)), |
186 | 0 | Box::new(err.substitute(subst)), |
187 | 0 | ), |
188 | 0 | MonoType::DataFrame(columns) => MonoType::DataFrame( |
189 | 0 | columns |
190 | 0 | .iter() |
191 | 0 | .map(|(name, ty)| (name.clone(), ty.substitute(subst))) |
192 | 0 | .collect(), |
193 | | ), |
194 | 0 | MonoType::Series(dtype) => MonoType::Series(Box::new(dtype.substitute(subst))), |
195 | 0 | MonoType::Reference(inner) => MonoType::Reference(Box::new(inner.substitute(subst))), |
196 | 0 | MonoType::Tuple(types) => { |
197 | 0 | MonoType::Tuple(types.iter().map(|ty| ty.substitute(subst)).collect()) |
198 | | } |
199 | 0 | _ => self.clone(), |
200 | | } |
201 | 0 | } |
202 | | |
203 | | /// Get free type variables in this type |
204 | | #[must_use] |
205 | 0 | pub fn free_vars(&self) -> Vec<TyVar> { |
206 | | use std::collections::HashSet; |
207 | | |
208 | 0 | fn collect_vars(ty: &MonoType, vars: &mut HashSet<TyVar>) { |
209 | 0 | match ty { |
210 | 0 | MonoType::Var(v) => { |
211 | 0 | vars.insert(v.clone()); |
212 | 0 | } |
213 | 0 | MonoType::Function(arg, ret) => { |
214 | 0 | collect_vars(arg, vars); |
215 | 0 | collect_vars(ret, vars); |
216 | 0 | } |
217 | 0 | MonoType::List(elem) => collect_vars(elem, vars), |
218 | 0 | MonoType::Optional(inner) |
219 | 0 | | MonoType::Series(inner) |
220 | 0 | | MonoType::Reference(inner) => { |
221 | 0 | collect_vars(inner, vars); |
222 | 0 | } |
223 | 0 | MonoType::Result(ok, err) => { |
224 | 0 | collect_vars(ok, vars); |
225 | 0 | collect_vars(err, vars); |
226 | 0 | } |
227 | 0 | MonoType::DataFrame(columns) => { |
228 | 0 | for (_, ty) in columns { |
229 | 0 | collect_vars(ty, vars); |
230 | 0 | } |
231 | | } |
232 | 0 | MonoType::Tuple(types) => { |
233 | 0 | for ty in types { |
234 | 0 | collect_vars(ty, vars); |
235 | 0 | } |
236 | | } |
237 | 0 | _ => {} |
238 | | } |
239 | 0 | } |
240 | | |
241 | 0 | let mut vars = HashSet::new(); |
242 | 0 | collect_vars(self, &mut vars); |
243 | 0 | vars.into_iter().collect() |
244 | 0 | } |
245 | | } |
246 | | |
247 | | #[cfg(test)] |
248 | | #[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)] |
249 | | #[allow(clippy::unwrap_used, clippy::panic)] |
250 | | mod tests { |
251 | | use super::*; |
252 | | |
253 | | #[test] |
254 | | fn test_type_display() { |
255 | | assert_eq!(MonoType::Int.to_string(), "i32"); |
256 | | assert_eq!(MonoType::Bool.to_string(), "bool"); |
257 | | assert_eq!( |
258 | | MonoType::Function(Box::new(MonoType::Int), Box::new(MonoType::Bool)).to_string(), |
259 | | "(i32 -> bool)" |
260 | | ); |
261 | | assert_eq!(MonoType::List(Box::new(MonoType::Int)).to_string(), "[i32]"); |
262 | | } |
263 | | |
264 | | #[test] |
265 | | fn test_type_scheme_instantiation() { |
266 | | let mut gen = TyVarGenerator::new(); |
267 | | let var = gen.fresh(); |
268 | | |
269 | | let scheme = TypeScheme { |
270 | | vars: vec![var.clone()], |
271 | | ty: MonoType::Function( |
272 | | Box::new(MonoType::Var(var.clone())), |
273 | | Box::new(MonoType::Var(var)), |
274 | | ), |
275 | | }; |
276 | | |
277 | | let instantiated = scheme.instantiate(&mut gen); |
278 | | match instantiated { |
279 | | MonoType::Function(arg, ret) => { |
280 | | assert!(matches!(*arg, MonoType::Var(_))); |
281 | | assert!(matches!(*ret, MonoType::Var(_))); |
282 | | } |
283 | | _ => panic!("Expected function type"), |
284 | | } |
285 | | } |
286 | | |
287 | | #[test] |
288 | | fn test_substitution() { |
289 | | let mut subst = HashMap::new(); |
290 | | let var = TyVar(0); |
291 | | subst.insert(var.clone(), MonoType::Int); |
292 | | |
293 | | let ty = MonoType::List(Box::new(MonoType::Var(var))); |
294 | | let result = ty.substitute(&subst); |
295 | | |
296 | | assert_eq!(result, MonoType::List(Box::new(MonoType::Int))); |
297 | | } |
298 | | |
299 | | #[test] |
300 | | fn test_free_vars() { |
301 | | let var1 = TyVar(0); |
302 | | let var2 = TyVar(1); |
303 | | |
304 | | let ty = MonoType::Function( |
305 | | Box::new(MonoType::Var(var1.clone())), |
306 | | Box::new(MonoType::List(Box::new(MonoType::Var(var2.clone())))), |
307 | | ); |
308 | | |
309 | | let free = ty.free_vars(); |
310 | | assert_eq!(free.len(), 2); |
311 | | assert!(free.contains(&var1)); |
312 | | assert!(free.contains(&var2)); |
313 | | |
314 | | // Test that duplicate variables are deduplicated |
315 | | let ty_dup = MonoType::Function( |
316 | | Box::new(MonoType::Var(var1.clone())), |
317 | | Box::new(MonoType::Var(var1.clone())), |
318 | | ); |
319 | | let free_dup = ty_dup.free_vars(); |
320 | | assert_eq!(free_dup.len(), 1); |
321 | | assert!(free_dup.contains(&var1)); |
322 | | } |
323 | | } |