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}