rain_lang/value/
lambda.rs

1/*!
2Lambda functions and associated utilities
3*/
4
5use smallvec::{smallvec, SmallVec};
6use crate::graph::region::Region;
7use super::{
8    ValId, ValueEnum, ValueData, Value, ValueDesc,
9    error::{ValueError, IncomparableRegions, RegionAlreadyFused, RegionError},
10    expr::SMALL_SEXPR_SIZE
11};
12
13/// The size of a small set of results from a lambda expression
14pub const SMALL_RESULT_SIZE: usize = SMALL_SEXPR_SIZE;
15
16/// A lambda function
17#[derive(Debug, Clone, PartialEq, Hash, Eq)]
18pub struct Lambda {
19    /// The region owned by this lambda function
20    region: Region,
21    /// The results of this lambda function
22    results: SmallVec<[ValId; SMALL_RESULT_SIZE]>
23}
24
25impl Lambda {
26    /// Create a new lambda function with the given results from the given `Region`
27    pub fn new(region: Region, results: SmallVec<[ValId; SMALL_RESULT_SIZE]>)
28    -> Result<Lambda, RegionError> {
29        // Check region and results. If valid, fuse.
30        {
31            let mut params = region.params_mut();
32            if params.fused() { return Err(RegionAlreadyFused.into()) }
33            // Check all results are in the given region
34            for result in results.iter() {
35                let result_region = &result.data().region;
36                if !(result_region >= &region) {
37                    let err = IncomparableRegions(
38                        smallvec![region.downgrade(), result_region.clone()]
39                    );
40                    return Err(err.into())
41                }
42            }
43            params.fuse();
44        }
45        Ok(Lambda { region, results })
46    }
47    /// Get the region associated with this lambda function
48    pub fn region(&self) -> &Region { &self.region }
49    /// Get the result array of this lambda function
50    pub fn results(&self) -> &[ValId] { &self.results }
51    /// Get the dependencies of this lambda function
52    pub fn dependencies(&self) -> std::slice::Iter<ValId> { self.results().iter() }
53}
54
55impl From<Lambda> for ValueEnum {
56    fn from(lambda: Lambda) -> ValueEnum { ValueEnum::Lambda(lambda) }
57}
58
59impl From<Lambda> for ValueData {
60    fn from(lambda: Lambda) -> ValueData {
61        let region = lambda.region().parent.clone();
62        ValueData::with_region(lambda.into(), region)
63    }
64}
65
66impl From<Lambda> for ValId {
67    fn from(lambda: Lambda) -> ValId {
68        ValId::try_new(ValueData::from(lambda)).expect("Impossible")
69    }
70}
71
72impl ValueDesc for Lambda {
73    type Err = ValueError;
74}
75
76impl Value for Lambda {}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use crate::value::primitive::logical::Bool;
82    use crate::assert_jeq;
83
84    #[test]
85    fn invalid_nested_lambda_result_fails() {
86        let region = Region::new();
87        let nested_region = Region::new_in(region.downgrade());
88        let (iy, y) = nested_region.add_with_ty(Bool.into());
89        assert_eq!(iy, 0);
90        let err = Lambda::new(region.clone(), smallvec![y.clone()])
91            .expect_err("This is an invalid function");
92        match err {
93            RegionError::IncomparableRegions(err) => {
94                assert!(
95                    err.0.as_slice() == &[region.downgrade(), nested_region.downgrade()]
96                    || err.0.as_slice() == &[nested_region.downgrade(), region.downgrade()]
97                )
98            },
99            err => panic!("Wrong error {:?} (expected incomparable regions)", err)
100        }
101    }
102
103    #[test]
104    fn lambda_on_fused_region_fails() {
105        let region = Region::new();
106        { region.params_mut().fuse(); }
107        let t = ValId::from(true);
108        let err = Lambda::new(region.clone(), smallvec![t.clone()])
109            .expect_err("This function's region is invalid");
110        match err {
111            RegionError::RegionAlreadyFused(_) => {},
112            err => panic!("Wrong error {:?} (expected region already fused)", err)
113        }
114    }
115
116    #[test]
117    fn constant_bool_lambda() {
118        let region = Region::new();
119        let t = ValId::from(true);
120        let lambda = Lambda::new(region.clone(), smallvec![t.clone()])
121            .expect("This is a valid function");
122        assert_eq!(lambda.region(), &region);
123        assert_eq!(lambda.results().len(), 1);
124        assert_jeq!(lambda.results()[0], &t);
125        let lambda = ValId::from(lambda);
126        //TODO: application
127        let _ = lambda;
128    }
129
130    #[test]
131    fn identity_bool_lambda() {
132        let region = Region::new();
133        let (_, x) = region.add_with_ty(Bool.into());
134        let lambda = Lambda::new(region.clone(), smallvec![x.clone()])
135            .expect("This is a valid function");
136        assert_eq!(lambda.region(), &region);
137        assert_eq!(lambda.results().len(), 1);
138        assert_eq!(lambda.results()[0], x);
139        let lambda = ValId::from(lambda);
140        //TODO: application
141        let _ = lambda;
142    }
143}