ruchy/middleend/types.rs
1//! Type system representation for Ruchy
2//!
3//! This module implements the Hindley-Milner type system for Ruchy, providing
4//! type inference, unification, and polymorphic type schemes.
5//!
6//! # Examples
7//!
8//! ```
9//! use ruchy::middleend::types::{MonoType, TypeScheme, TyVarGenerator};
10//!
11//! // Create basic types
12//! let int_type = MonoType::Int;
13//! let bool_type = MonoType::Bool;
14//!
15//! // Create function type: i32 -> bool
16//! let func_type = MonoType::Function(
17//! Box::new(int_type),
18//! Box::new(bool_type)
19//! );
20//!
21//! // Create type scheme for polymorphic function
22//! let mut gen = TyVarGenerator::new();
23//! let var = gen.fresh();
24//! let scheme = TypeScheme::mono(MonoType::Var(var));
25//! ```
26//!
27//! # Type System Features
28//!
29//! - **Type Variables**: For unification and type inference
30//! - **Monomorphic Types**: Concrete types without quantification
31//! - **Type Schemes**: Polymorphic types with quantified variables
32//! - **Substitution**: Type variable replacement mechanism
33//! - **`DataFrame` Types**: Support for data science operations
34use std::collections::HashMap;
35use std::fmt;
36/// Type variable for unification
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38pub struct TyVar(pub u32);
39impl fmt::Display for TyVar {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "τ{}", self.0)
42 }
43}
44/// Monomorphic types in the Hindley-Milner system
45#[derive(Debug, Clone, PartialEq)]
46pub enum MonoType {
47 /// Type variable (unknown type to be inferred)
48 Var(TyVar),
49 /// Primitive integer type
50 Int,
51 /// Primitive float type
52 Float,
53 /// Primitive boolean type
54 Bool,
55 /// String type
56 String,
57 /// Character type
58 Char,
59 /// Unit type ()
60 Unit,
61 /// Function type: T1 -> T2
62 Function(Box<MonoType>, Box<MonoType>),
63 /// List type: `[T]`
64 List(Box<MonoType>),
65 /// Tuple type: (T1, T2, ...)
66 Tuple(Vec<MonoType>),
67 /// Optional type: T?
68 Optional(Box<MonoType>),
69 /// Result type: Result<T, E>
70 Result(Box<MonoType>, Box<MonoType>),
71 /// Named type (user-defined or gradual typing 'Any')
72 Named(String),
73 /// Reference type: &T
74 Reference(Box<MonoType>),
75 /// `DataFrame` type with column names and their types
76 DataFrame(Vec<(String, MonoType)>),
77 /// Series type with element type
78 Series(Box<MonoType>),
79}
80impl fmt::Display for MonoType {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 MonoType::Var(v) => write!(f, "{v}"),
84 MonoType::Int => write!(f, "i32"),
85 MonoType::Float => write!(f, "f64"),
86 MonoType::Bool => write!(f, "bool"),
87 MonoType::String => write!(f, "String"),
88 MonoType::Char => write!(f, "char"),
89 MonoType::Unit => write!(f, "()"),
90 MonoType::Function(arg, ret) => write!(f, "({arg} -> {ret})"),
91 MonoType::List(elem) => write!(f, "[{elem}]"),
92 MonoType::Optional(inner) => write!(f, "{inner}?"),
93 MonoType::Result(ok, err) => write!(f, "Result<{ok}, {err}>"),
94 MonoType::Tuple(types) => {
95 write!(f, "(")?;
96 for (i, ty) in types.iter().enumerate() {
97 if i > 0 {
98 write!(f, ", ")?;
99 }
100 write!(f, "{ty}")?;
101 }
102 write!(f, ")")
103 }
104 MonoType::Named(name) => write!(f, "{name}"),
105 MonoType::Reference(inner) => write!(f, "&{inner}"),
106 MonoType::DataFrame(columns) => {
107 write!(f, "DataFrame[")?;
108 for (i, (name, ty)) in columns.iter().enumerate() {
109 if i > 0 {
110 write!(f, ", ")?;
111 }
112 write!(f, "{name}: {ty}")?;
113 }
114 write!(f, "]")
115 }
116 MonoType::Series(dtype) => write!(f, "Series<{dtype}>"),
117 }
118 }
119}
120/// Polymorphic type scheme: ∀α₁...αₙ. τ
121#[derive(Debug, Clone, PartialEq)]
122pub struct TypeScheme {
123 /// Quantified type variables
124 pub vars: Vec<TyVar>,
125 /// The monomorphic type
126 pub ty: MonoType,
127}
128impl TypeScheme {
129 /// Create a monomorphic type scheme (no quantified variables)
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// use ruchy::middleend::types::{MonoType, TypeScheme};
135 ///
136 /// let scheme = TypeScheme::mono(MonoType::Int);
137 /// assert_eq!(scheme.vars.len(), 0);
138 /// assert_eq!(scheme.ty, MonoType::Int);
139 /// ```
140 #[must_use]
141 pub fn mono(ty: MonoType) -> Self {
142 TypeScheme {
143 vars: Vec::new(),
144 ty,
145 }
146 }
147 /// Instantiate a type scheme with fresh type variables
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use ruchy::middleend::types::{MonoType, TypeScheme, TyVarGenerator, TyVar};
153 ///
154 /// let mut gen = TyVarGenerator::new();
155 /// let var = gen.fresh();
156 /// let scheme = TypeScheme {
157 /// vars: vec![var.clone()],
158 /// ty: MonoType::Var(var)
159 /// };
160 /// let instance = scheme.instantiate(&mut gen);
161 /// // Should get a fresh type variable
162 /// assert!(matches!(instance, MonoType::Var(_)));
163 /// ```
164 pub fn instantiate(&self, gen: &mut TyVarGenerator) -> MonoType {
165 if self.vars.is_empty() {
166 self.ty.clone()
167 } else {
168 let subst: HashMap<TyVar, MonoType> = self
169 .vars
170 .iter()
171 .map(|v| (v.clone(), MonoType::Var(gen.fresh())))
172 .collect();
173 self.ty.substitute(&subst)
174 }
175 }
176
177 /// Generalize a monomorphic type into a type scheme
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// use ruchy::middleend::types::{MonoType, TypeScheme, TyVar};
183 /// use ruchy::middleend::environment::TypeEnv;
184 ///
185 /// let var = TyVar(0);
186 /// let mono_type = MonoType::Function(
187 /// Box::new(MonoType::Var(var.clone())),
188 /// Box::new(MonoType::Var(var.clone()))
189 /// );
190 /// let env = TypeEnv::new();
191 /// let scheme = TypeScheme::generalize(&env, &mono_type);
192 /// assert_eq!(scheme.vars.len(), 1);
193 /// assert!(scheme.vars.contains(&var));
194 /// ```
195 pub fn generalize(env: &crate::middleend::environment::TypeEnv, ty: &MonoType) -> Self {
196 let env_vars = env.free_vars();
197 let ty_vars = ty.free_vars();
198 let generalized_vars: Vec<TyVar> = ty_vars
199 .into_iter()
200 .filter(|v| !env_vars.contains(v))
201 .collect();
202 TypeScheme {
203 vars: generalized_vars,
204 ty: ty.clone(),
205 }
206 }
207}
208impl fmt::Display for TypeScheme {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 if self.vars.is_empty() {
211 write!(f, "{}", self.ty)
212 } else {
213 write!(f, "∀")?;
214 for (i, var) in self.vars.iter().enumerate() {
215 if i > 0 {
216 write!(f, ",")?;
217 }
218 write!(f, "{var}")?;
219 }
220 write!(f, ". {}", self.ty)
221 }
222 }
223}
224/// Type variable generator for fresh variables
225pub struct TyVarGenerator {
226 next: u32,
227}
228impl TyVarGenerator {
229 /// Create a new type variable generator
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use ruchy::middleend::types::TyVarGenerator;
235 ///
236 /// let gen = TyVarGenerator::new();
237 /// // Generator starts with id 0
238 /// ```
239 #[must_use]
240 pub fn new() -> Self {
241 TyVarGenerator { next: 0 }
242 }
243 /// Generate a fresh type variable
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use ruchy::middleend::types::{TyVarGenerator, TyVar};
249 ///
250 /// let mut gen = TyVarGenerator::new();
251 /// let var1 = gen.fresh();
252 /// let var2 = gen.fresh();
253 /// assert_eq!(var1, TyVar(0));
254 /// assert_eq!(var2, TyVar(1));
255 /// ```
256 pub fn fresh(&mut self) -> TyVar {
257 let var = TyVar(self.next);
258 self.next += 1;
259 var
260 }
261}
262impl Default for TyVarGenerator {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267/// Substitution mapping from type variables to types
268pub type Substitution = HashMap<TyVar, MonoType>;
269impl MonoType {
270 /// Apply a substitution to this type
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use std::collections::HashMap;
276 /// use ruchy::middleend::types::{MonoType, TyVar};
277 ///
278 /// let mut subst = HashMap::new();
279 /// let var = TyVar(0);
280 /// subst.insert(var.clone(), MonoType::Int);
281 ///
282 /// let list_type = MonoType::List(Box::new(MonoType::Var(var)));
283 /// let result = list_type.substitute(&subst);
284 /// assert_eq!(result, MonoType::List(Box::new(MonoType::Int)));
285 /// ```
286 #[must_use]
287 pub fn substitute(&self, subst: &Substitution) -> MonoType {
288 match self {
289 MonoType::Var(v) => subst.get(v).cloned().unwrap_or_else(|| self.clone()),
290 MonoType::Function(arg, ret) => MonoType::Function(
291 Box::new(arg.substitute(subst)),
292 Box::new(ret.substitute(subst)),
293 ),
294 MonoType::List(elem) => MonoType::List(Box::new(elem.substitute(subst))),
295 MonoType::Optional(inner) => MonoType::Optional(Box::new(inner.substitute(subst))),
296 MonoType::Result(ok, err) => MonoType::Result(
297 Box::new(ok.substitute(subst)),
298 Box::new(err.substitute(subst)),
299 ),
300 MonoType::DataFrame(columns) => MonoType::DataFrame(
301 columns
302 .iter()
303 .map(|(name, ty)| (name.clone(), ty.substitute(subst)))
304 .collect(),
305 ),
306 MonoType::Series(dtype) => MonoType::Series(Box::new(dtype.substitute(subst))),
307 MonoType::Reference(inner) => MonoType::Reference(Box::new(inner.substitute(subst))),
308 MonoType::Tuple(types) => {
309 MonoType::Tuple(types.iter().map(|ty| ty.substitute(subst)).collect())
310 }
311 _ => self.clone(),
312 }
313 }
314 /// Get free type variables in this type
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// use ruchy::middleend::types::{MonoType, TyVar};
320 ///
321 /// let var1 = TyVar(0);
322 /// let var2 = TyVar(1);
323 /// let func_type = MonoType::Function(
324 /// Box::new(MonoType::Var(var1.clone())),
325 /// Box::new(MonoType::Var(var2.clone()))
326 /// );
327 ///
328 /// let free_vars = func_type.free_vars();
329 /// assert_eq!(free_vars.len(), 2);
330 /// assert!(free_vars.contains(&var1));
331 /// assert!(free_vars.contains(&var2));
332 /// ```
333 #[must_use]
334 pub fn free_vars(&self) -> Vec<TyVar> {
335 use std::collections::HashSet;
336 fn collect_vars(ty: &MonoType, vars: &mut HashSet<TyVar>) {
337 match ty {
338 MonoType::Var(v) => {
339 vars.insert(v.clone());
340 }
341 MonoType::Function(arg, ret) => {
342 collect_vars(arg, vars);
343 collect_vars(ret, vars);
344 }
345 MonoType::List(elem) => collect_vars(elem, vars),
346 MonoType::Optional(inner)
347 | MonoType::Series(inner)
348 | MonoType::Reference(inner) => {
349 collect_vars(inner, vars);
350 }
351 MonoType::Result(ok, err) => {
352 collect_vars(ok, vars);
353 collect_vars(err, vars);
354 }
355 MonoType::DataFrame(columns) => {
356 for (_, ty) in columns {
357 collect_vars(ty, vars);
358 }
359 }
360 MonoType::Tuple(types) => {
361 for ty in types {
362 collect_vars(ty, vars);
363 }
364 }
365 _ => {}
366 }
367 }
368 let mut vars = HashSet::new();
369 collect_vars(self, &mut vars);
370 vars.into_iter().collect()
371 }
372}
373#[cfg(test)]
374#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
375mod tests {
376 use super::*;
377 #[test]
378 fn test_type_display() {
379 assert_eq!(MonoType::Int.to_string(), "i32");
380 assert_eq!(MonoType::Bool.to_string(), "bool");
381 assert_eq!(
382 MonoType::Function(Box::new(MonoType::Int), Box::new(MonoType::Bool)).to_string(),
383 "(i32 -> bool)"
384 );
385 assert_eq!(MonoType::List(Box::new(MonoType::Int)).to_string(), "[i32]");
386 }
387 #[test]
388 fn test_type_scheme_instantiation() {
389 let mut gen = TyVarGenerator::new();
390 let var = gen.fresh();
391 let scheme = TypeScheme {
392 vars: vec![var.clone()],
393 ty: MonoType::Function(
394 Box::new(MonoType::Var(var.clone())),
395 Box::new(MonoType::Var(var)),
396 ),
397 };
398 let instantiated = scheme.instantiate(&mut gen);
399 match instantiated {
400 MonoType::Function(arg, ret) => {
401 assert!(matches!(*arg, MonoType::Var(_)));
402 assert!(matches!(*ret, MonoType::Var(_)));
403 }
404 _ => panic!("Expected function type"),
405 }
406 }
407 #[test]
408 fn test_substitution() {
409 let mut subst = HashMap::new();
410 let var = TyVar(0);
411 subst.insert(var.clone(), MonoType::Int);
412 let ty = MonoType::List(Box::new(MonoType::Var(var)));
413 let result = ty.substitute(&subst);
414 assert_eq!(result, MonoType::List(Box::new(MonoType::Int)));
415 }
416 #[test]
417 fn test_free_vars() {
418 let var1 = TyVar(0);
419 let var2 = TyVar(1);
420 let ty = MonoType::Function(
421 Box::new(MonoType::Var(var1.clone())),
422 Box::new(MonoType::List(Box::new(MonoType::Var(var2.clone())))),
423 );
424 let free = ty.free_vars();
425 assert_eq!(free.len(), 2);
426 assert!(free.contains(&var1));
427 assert!(free.contains(&var2));
428 // Test that duplicate variables are deduplicated
429 let ty_dup = MonoType::Function(
430 Box::new(MonoType::Var(var1.clone())),
431 Box::new(MonoType::Var(var1.clone())),
432 );
433 let free_dup = ty_dup.free_vars();
434 assert_eq!(free_dup.len(), 1);
435 assert!(free_dup.contains(&var1));
436 }
437}
438#[cfg(test)]
439mod property_tests_types {
440 use proptest::proptest;
441
442 proptest! {
443 /// Property: Function never panics on any input
444 #[test]
445 fn test_mono_never_panics(input: String) {
446 // Limit input size to avoid timeout
447 let _input = if input.len() > 100 { &input[..100] } else { &input[..] };
448 // Function should not panic on any input
449 let _ = std::panic::catch_unwind(|| {
450 // Call function with various inputs
451 // This is a template - adjust based on actual function signature
452 });
453 }
454 }
455}