Skip to main content

tensorlogic_adapters/
parametric.rs

1//! Parameterized domain types for generic type definitions.
2//!
3//! This module provides support for parameterized domains such as:
4//! - `List<T>`: A list of elements of type T
5//! - `Option<T>`: An optional value of type T
6//! - `Pair<A, B>`: A pair of values of types A and B
7//! - `Map<K, V>`: A mapping from keys of type K to values of type V
8//!
9//! These parameterized types enable rich type definitions in the symbol table
10//! and support for complex data structures in logical rules.
11
12use serde::{Deserialize, Serialize};
13use std::fmt;
14
15use crate::error::AdapterError;
16
17/// A parameterized domain type with type parameters.
18///
19/// Examples:
20/// - `List<Person>` - A list of persons
21/// - `Option<City>` - An optional city
22/// - `Pair<Person, City>` - A pair of person and city
23/// - `Map<String, Int>` - A mapping from strings to integers
24#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub struct ParametricType {
26    /// The type constructor name (e.g., "List", "Option", "Pair")
27    pub constructor: String,
28    /// The type parameters (e.g., `["Person"]` for `List<Person>`)
29    pub parameters: Vec<TypeParameter>,
30}
31
32/// A type parameter that can be either a concrete type or another parametric type.
33#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
34pub enum TypeParameter {
35    /// A concrete type name (e.g., "Person", "Int")
36    Concrete(String),
37    /// A nested parametric type (e.g., `List<Option<Person>>`)
38    Parametric(Box<ParametricType>),
39}
40
41impl ParametricType {
42    /// Creates a new parametric type with the given constructor and parameters.
43    pub fn new(constructor: impl Into<String>, parameters: Vec<TypeParameter>) -> Self {
44        ParametricType {
45            constructor: constructor.into(),
46            parameters,
47        }
48    }
49
50    /// Creates a `List<T>` parametric type.
51    pub fn list(inner: TypeParameter) -> Self {
52        ParametricType::new("List", vec![inner])
53    }
54
55    /// Creates an `Option<T>` parametric type.
56    pub fn option(inner: TypeParameter) -> Self {
57        ParametricType::new("Option", vec![inner])
58    }
59
60    /// Creates a Pair<A, B> parametric type.
61    pub fn pair(first: TypeParameter, second: TypeParameter) -> Self {
62        ParametricType::new("Pair", vec![first, second])
63    }
64
65    /// Creates a Map<K, V> parametric type.
66    pub fn map(key: TypeParameter, value: TypeParameter) -> Self {
67        ParametricType::new("Map", vec![key, value])
68    }
69
70    /// Validates that the parametric type is well-formed.
71    ///
72    /// Checks:
73    /// - Constructor name is not empty
74    /// - Number of parameters matches expected arity for known constructors
75    /// - All nested parametric types are also well-formed
76    pub fn validate(&self) -> Result<(), AdapterError> {
77        if self.constructor.is_empty() {
78            return Err(AdapterError::InvalidParametricType(
79                "Constructor name cannot be empty".to_string(),
80            ));
81        }
82
83        // Validate arity for known constructors
84        let expected_arity = match self.constructor.as_str() {
85            "List" | "Option" | "Set" => 1,
86            "Pair" | "Map" | "Either" => 2,
87            _ => return Ok(()), // Unknown constructors are allowed
88        };
89
90        if self.parameters.len() != expected_arity {
91            return Err(AdapterError::InvalidParametricType(format!(
92                "Constructor '{}' expects {} parameters, found {}",
93                self.constructor,
94                expected_arity,
95                self.parameters.len()
96            )));
97        }
98
99        // Recursively validate nested parametric types
100        for param in &self.parameters {
101            if let TypeParameter::Parametric(nested) = param {
102                nested.validate()?;
103            }
104        }
105
106        Ok(())
107    }
108
109    /// Returns the arity (number of type parameters) of this parametric type.
110    pub fn arity(&self) -> usize {
111        self.parameters.len()
112    }
113
114    /// Checks if this is a monomorphic type (no type parameters).
115    pub fn is_monomorphic(&self) -> bool {
116        self.parameters.is_empty()
117    }
118
119    /// Checks if this parametric type contains the given type parameter name.
120    pub fn contains_parameter(&self, name: &str) -> bool {
121        self.parameters.iter().any(|param| match param {
122            TypeParameter::Concrete(n) => n == name,
123            TypeParameter::Parametric(nested) => nested.contains_parameter(name),
124        })
125    }
126
127    /// Substitutes all occurrences of a type parameter with a concrete type.
128    ///
129    /// This is used for type instantiation when applying a parametric type
130    /// to concrete arguments.
131    pub fn substitute(&self, from: &str, to: &TypeParameter) -> ParametricType {
132        let new_params = self
133            .parameters
134            .iter()
135            .map(|param| match param {
136                TypeParameter::Concrete(name) if name == from => to.clone(),
137                TypeParameter::Concrete(_) => param.clone(),
138                TypeParameter::Parametric(nested) => {
139                    TypeParameter::Parametric(Box::new(nested.substitute(from, to)))
140                }
141            })
142            .collect();
143
144        ParametricType {
145            constructor: self.constructor.clone(),
146            parameters: new_params,
147        }
148    }
149}
150
151impl TypeParameter {
152    /// Creates a concrete type parameter.
153    pub fn concrete(name: impl Into<String>) -> Self {
154        TypeParameter::Concrete(name.into())
155    }
156
157    /// Creates a parametric type parameter.
158    pub fn parametric(ptype: ParametricType) -> Self {
159        TypeParameter::Parametric(Box::new(ptype))
160    }
161
162    /// Returns the name if this is a concrete type parameter.
163    pub fn as_concrete(&self) -> Option<&str> {
164        match self {
165            TypeParameter::Concrete(name) => Some(name),
166            TypeParameter::Parametric(_) => None,
167        }
168    }
169
170    /// Returns the parametric type if this is a parametric type parameter.
171    pub fn as_parametric(&self) -> Option<&ParametricType> {
172        match self {
173            TypeParameter::Concrete(_) => None,
174            TypeParameter::Parametric(ptype) => Some(ptype),
175        }
176    }
177}
178
179impl fmt::Display for ParametricType {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        write!(f, "{}", self.constructor)?;
182        if !self.parameters.is_empty() {
183            write!(f, "<")?;
184            for (i, param) in self.parameters.iter().enumerate() {
185                if i > 0 {
186                    write!(f, ", ")?;
187                }
188                write!(f, "{}", param)?;
189            }
190            write!(f, ">")?;
191        }
192        Ok(())
193    }
194}
195
196impl fmt::Display for TypeParameter {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        match self {
199            TypeParameter::Concrete(name) => write!(f, "{}", name),
200            TypeParameter::Parametric(ptype) => write!(f, "{}", ptype),
201        }
202    }
203}
204
205/// A type bound that constrains a type parameter.
206///
207/// Type bounds allow specifying constraints on type parameters, such as:
208/// - `T: Comparable` - T must be a comparable type
209/// - `T: Numeric` - T must be a numeric type
210/// - `T: Subtypes(Person)` - T must be a subtype of Person
211#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
212pub struct TypeBound {
213    /// The name of the type parameter being constrained
214    pub param_name: String,
215    /// The constraint kind
216    pub constraint: BoundConstraint,
217}
218
219/// The kind of constraint in a type bound.
220#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
221pub enum BoundConstraint {
222    /// The type must be a subtype of the given type
223    Subtype(String),
224    /// The type must implement the given trait
225    Trait(String),
226    /// The type must be comparable (supports equality, ordering)
227    Comparable,
228    /// The type must be numeric (supports arithmetic operations)
229    Numeric,
230}
231
232impl TypeBound {
233    /// Creates a new type bound with the given constraint.
234    pub fn new(param_name: impl Into<String>, constraint: BoundConstraint) -> Self {
235        TypeBound {
236            param_name: param_name.into(),
237            constraint,
238        }
239    }
240
241    /// Creates a subtype bound: `T: Subtypes(supertype)`
242    pub fn subtype(param_name: impl Into<String>, supertype: impl Into<String>) -> Self {
243        TypeBound::new(param_name, BoundConstraint::Subtype(supertype.into()))
244    }
245
246    /// Creates a trait bound: `T: Trait(trait_name)`
247    pub fn trait_bound(param_name: impl Into<String>, trait_name: impl Into<String>) -> Self {
248        TypeBound::new(param_name, BoundConstraint::Trait(trait_name.into()))
249    }
250
251    /// Creates a comparable bound: `T: Comparable`
252    pub fn comparable(param_name: impl Into<String>) -> Self {
253        TypeBound::new(param_name, BoundConstraint::Comparable)
254    }
255
256    /// Creates a numeric bound: `T: Numeric`
257    pub fn numeric(param_name: impl Into<String>) -> Self {
258        TypeBound::new(param_name, BoundConstraint::Numeric)
259    }
260}
261
262impl fmt::Display for TypeBound {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "{}: ", self.param_name)?;
265        match &self.constraint {
266            BoundConstraint::Subtype(s) => write!(f, "Subtype({})", s),
267            BoundConstraint::Trait(t) => write!(f, "{}", t),
268            BoundConstraint::Comparable => write!(f, "Comparable"),
269            BoundConstraint::Numeric => write!(f, "Numeric"),
270        }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[test]
279    fn test_parametric_type_list() {
280        let list_person = ParametricType::list(TypeParameter::concrete("Person"));
281        assert_eq!(list_person.constructor, "List");
282        assert_eq!(list_person.arity(), 1);
283        assert_eq!(list_person.to_string(), "List<Person>");
284    }
285
286    #[test]
287    fn test_parametric_type_option() {
288        let opt_city = ParametricType::option(TypeParameter::concrete("City"));
289        assert_eq!(opt_city.constructor, "Option");
290        assert_eq!(opt_city.arity(), 1);
291        assert_eq!(opt_city.to_string(), "Option<City>");
292    }
293
294    #[test]
295    fn test_parametric_type_pair() {
296        let pair = ParametricType::pair(
297            TypeParameter::concrete("Person"),
298            TypeParameter::concrete("City"),
299        );
300        assert_eq!(pair.constructor, "Pair");
301        assert_eq!(pair.arity(), 2);
302        assert_eq!(pair.to_string(), "Pair<Person, City>");
303    }
304
305    #[test]
306    fn test_parametric_type_map() {
307        let map = ParametricType::map(
308            TypeParameter::concrete("String"),
309            TypeParameter::concrete("Int"),
310        );
311        assert_eq!(map.constructor, "Map");
312        assert_eq!(map.arity(), 2);
313        assert_eq!(map.to_string(), "Map<String, Int>");
314    }
315
316    #[test]
317    fn test_nested_parametric_type() {
318        let list_option_person = ParametricType::list(TypeParameter::parametric(
319            ParametricType::option(TypeParameter::concrete("Person")),
320        ));
321        assert_eq!(list_option_person.to_string(), "List<Option<Person>>");
322        assert!(list_option_person.contains_parameter("Person"));
323    }
324
325    #[test]
326    fn test_parametric_type_validation() {
327        let valid = ParametricType::list(TypeParameter::concrete("Person"));
328        assert!(valid.validate().is_ok());
329
330        let invalid = ParametricType::new(
331            "List",
332            vec![TypeParameter::concrete("A"), TypeParameter::concrete("B")],
333        );
334        assert!(invalid.validate().is_err());
335    }
336
337    #[test]
338    fn test_parametric_type_substitution() {
339        let list_t = ParametricType::list(TypeParameter::concrete("T"));
340        let list_person = list_t.substitute("T", &TypeParameter::concrete("Person"));
341        assert_eq!(list_person.to_string(), "List<Person>");
342    }
343
344    #[test]
345    fn test_type_bounds() {
346        let bound = TypeBound::subtype("T", "Person");
347        assert_eq!(bound.param_name, "T");
348        assert_eq!(bound.to_string(), "T: Subtype(Person)");
349
350        let comparable = TypeBound::comparable("T");
351        assert_eq!(comparable.to_string(), "T: Comparable");
352
353        let numeric = TypeBound::numeric("N");
354        assert_eq!(numeric.to_string(), "N: Numeric");
355    }
356}