lambda_calculus/
reduction.rs

1//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s
2
3pub use self::Order::*;
4use crate::term::Term::*;
5use crate::term::{Term, TermError};
6use std::{cmp, fmt, mem};
7
8/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) of
9/// β-reductions.
10///
11/// - the `NOR`, `HNO`, `APP` and `HAP` orders reduce expressions to their normal form
12/// - the `APP` order will fail to fully reduce expressions containing terms without a normal form,
13///   e.g. the `Y` combinator (they will expand forever)
14/// - the `CBN` order reduces to weak head normal form
15/// - the `CBV` order reduces to weak normal form
16/// - the `HSP` order reduces to head normal form
17#[derive(Debug, PartialEq, Eq, Clone, Copy)]
18pub enum Order {
19    /// normal - leftmost outermost; the most popular reduction strategy
20    NOR,
21    /// call-by-name - leftmost outermost, no reductions inside abstractions
22    CBN,
23    /// head spine - leftmost outermost, abstractions reduced only in head position
24    HSP,
25    /// hybrid normal - a mix between `HSP` (head spine) and `NOR` (normal) strategies
26    HNO,
27    /// applicative - leftmost innermost; the most eager strategy; unfit for recursion combinators
28    APP,
29    /// call-by-value - leftmost innermost, no reductions inside abstractions
30    CBV,
31    /// hybrid applicative - a mix between `CBV` (call-by-value) and `APP` (applicative) strategies;
32    /// usually the fastest-reducing normalizing strategy
33    HAP,
34}
35
36/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
37/// the number of reductions (`0` means no limit) and returns the reduced `Term`.
38///
39/// # Example
40///
41/// ```
42/// use lambda_calculus::*;
43///
44/// let expr    = parse(&"(λa.λb.λc.a (λd.λe.e (d b)) (λd.c) (λd.d)) (λa.λb.a b)", Classic).unwrap();
45/// let reduced = parse(&"λa.λb.b", Classic).unwrap();
46///
47/// assert_eq!(beta(expr, NOR, 0), reduced);
48/// ```
49pub fn beta(mut term: Term, order: Order, limit: usize) -> Term {
50    term.reduce(order, limit);
51    term
52}
53
54impl Term {
55    /// Applies a `Term` to `self` via substitution and variable update.
56    ///
57    /// # Example
58    /// ```
59    /// use lambda_calculus::*;
60    ///
61    /// let mut term1  = parse(&"λλ42(λ13)", DeBruijn).unwrap();
62    /// let term2      = parse(&"λ51", DeBruijn).unwrap();
63    /// let result     = parse(&"λ3(λ61)(λ1(λ71))", DeBruijn).unwrap();
64    ///
65    /// term1.apply(&term2);
66    ///
67    /// assert_eq!(term1, result);
68    /// ```
69    /// # Errors
70    ///
71    /// Returns a `TermError` if `self` is not an `Abs`traction.
72    pub fn apply(&mut self, rhs: &Term) -> Result<(), TermError> {
73        self.unabs_ref()?;
74
75        self._apply(rhs, 0);
76
77        let ret = mem::replace(self, Var(0)); // replace self with a dummy
78        *self = ret.unabs().unwrap(); // move unabstracted self back to its place
79
80        Ok(())
81    }
82
83    fn _apply(&mut self, rhs: &Term, depth: usize) {
84        match self {
85            Var(i) => match (*i).cmp(&depth) {
86                cmp::Ordering::Equal => {
87                    rhs.clone_into(self); // substitute a top-level variable from lhs with rhs
88                    self.update_free_variables(depth - 1, 0); // update indices of free variables from rhs
89                }
90                cmp::Ordering::Greater => {
91                    *self = Var(*i - 1); // decrement a free variable's index
92                }
93                _ => {}
94            },
95            Abs(ref mut abstracted) => abstracted._apply(rhs, depth + 1),
96            App(boxed) => {
97                let (ref mut lhs_lhs, ref mut lhs_rhs) = **boxed;
98                lhs_lhs._apply(rhs, depth);
99                lhs_rhs._apply(rhs, depth)
100            }
101        }
102    }
103
104    fn update_free_variables(&mut self, added_depth: usize, own_depth: usize) {
105        match self {
106            Var(ref mut i) => {
107                if *i > own_depth {
108                    *i += added_depth
109                }
110            }
111            Abs(ref mut abstracted) => abstracted.update_free_variables(added_depth, own_depth + 1),
112            App(boxed) => {
113                let (ref mut lhs, ref mut rhs) = **boxed;
114                lhs.update_free_variables(added_depth, own_depth);
115                rhs.update_free_variables(added_depth, own_depth)
116            }
117        }
118    }
119
120    fn eval(&mut self, count: &mut usize) {
121        let to_apply = mem::replace(self, Var(0)); // replace self with a dummy
122        let (mut lhs, rhs) = to_apply.unapp().unwrap(); // safe; only called in reduction sites
123        lhs.apply(&rhs).unwrap(); // ditto
124        *self = lhs; // move self back to its place
125
126        *count += 1;
127    }
128
129    fn is_reducible(&self, limit: usize, count: usize) -> bool {
130        self.lhs_ref().and_then(|t| t.unabs_ref()).is_ok() && (limit == 0 || count < limit)
131    }
132
133    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit
134    /// on the number of reductions (`0` means no limit) and returns the number of performed
135    /// reductions.
136    ///
137    /// # Example
138    ///
139    /// ```
140    /// use lambda_calculus::*;
141    ///
142    /// let mut expression = parse(&"(λa.λb.λc.b (a b c)) (λa.λb.b)", Classic).unwrap();
143    /// let reduced        = parse(&"λa.λb.a b", Classic).unwrap();
144    ///
145    /// expression.reduce(NOR, 0);
146    ///
147    /// assert_eq!(expression, reduced);
148    /// ```
149    pub fn reduce(&mut self, order: Order, limit: usize) -> usize {
150        let mut count = 0;
151
152        match order {
153            CBN => self.beta_cbn(limit, &mut count),
154            NOR => self.beta_nor(limit, &mut count),
155            CBV => self.beta_cbv(limit, &mut count),
156            APP => self.beta_app(limit, &mut count),
157            HSP => self.beta_hsp(limit, &mut count),
158            HNO => self.beta_hno(limit, &mut count),
159            HAP => self.beta_hap(limit, &mut count),
160        }
161
162        count
163    }
164
165    fn beta_cbn(&mut self, limit: usize, count: &mut usize) {
166        if limit != 0 && *count == limit {
167            return;
168        }
169
170        if let App(_) = *self {
171            self.lhs_mut().unwrap().beta_cbn(limit, count);
172
173            if self.is_reducible(limit, *count) {
174                self.eval(count);
175                self.beta_cbn(limit, count);
176            }
177        }
178    }
179
180    fn beta_nor(&mut self, limit: usize, count: &mut usize) {
181        if limit != 0 && *count == limit {
182            return;
183        }
184
185        match *self {
186            Abs(ref mut abstracted) => abstracted.beta_nor(limit, count),
187            App(_) => {
188                self.lhs_mut().unwrap().beta_cbn(limit, count);
189
190                if self.is_reducible(limit, *count) {
191                    self.eval(count);
192                    self.beta_nor(limit, count);
193                } else {
194                    self.lhs_mut().unwrap().beta_nor(limit, count);
195                    self.rhs_mut().unwrap().beta_nor(limit, count);
196                }
197            }
198            _ => (),
199        }
200    }
201
202    fn beta_cbv(&mut self, limit: usize, count: &mut usize) {
203        if limit != 0 && *count == limit {
204            return;
205        }
206
207        if let App(_) = *self {
208            self.lhs_mut().unwrap().beta_cbv(limit, count);
209            self.rhs_mut().unwrap().beta_cbv(limit, count);
210
211            if self.is_reducible(limit, *count) {
212                self.eval(count);
213                self.beta_cbv(limit, count);
214            }
215        }
216    }
217
218    fn beta_app(&mut self, limit: usize, count: &mut usize) {
219        if limit != 0 && *count == limit {
220            return;
221        }
222
223        match *self {
224            Abs(ref mut abstracted) => abstracted.beta_app(limit, count),
225            App(_) => {
226                self.lhs_mut().unwrap().beta_app(limit, count);
227                self.rhs_mut().unwrap().beta_app(limit, count);
228
229                if self.is_reducible(limit, *count) {
230                    self.eval(count);
231                    self.beta_app(limit, count);
232                }
233            }
234            _ => (),
235        }
236    }
237
238    fn beta_hap(&mut self, limit: usize, count: &mut usize) {
239        if limit != 0 && *count == limit {
240            return;
241        }
242
243        match *self {
244            Abs(ref mut abstracted) => abstracted.beta_hap(limit, count),
245            App(_) => {
246                self.lhs_mut().unwrap().beta_cbv(limit, count);
247                self.rhs_mut().unwrap().beta_hap(limit, count);
248
249                if self.is_reducible(limit, *count) {
250                    self.eval(count);
251                    self.beta_hap(limit, count);
252                } else {
253                    self.lhs_mut().unwrap().beta_hap(limit, count);
254                }
255            }
256            _ => (),
257        }
258    }
259
260    fn beta_hsp(&mut self, limit: usize, count: &mut usize) {
261        if limit != 0 && *count == limit {
262            return;
263        }
264
265        match *self {
266            Abs(ref mut abstracted) => abstracted.beta_hsp(limit, count),
267            App(_) => {
268                self.lhs_mut().unwrap().beta_hsp(limit, count);
269
270                if self.is_reducible(limit, *count) {
271                    self.eval(count);
272                    self.beta_hsp(limit, count)
273                }
274            }
275            _ => (),
276        }
277    }
278
279    fn beta_hno(&mut self, limit: usize, count: &mut usize) {
280        if limit != 0 && *count == limit {
281            return;
282        }
283
284        match *self {
285            Abs(ref mut abstracted) => abstracted.beta_hno(limit, count),
286            App(_) => {
287                self.lhs_mut().unwrap().beta_hsp(limit, count);
288
289                if self.is_reducible(limit, *count) {
290                    self.eval(count);
291                    self.beta_hno(limit, count)
292                } else {
293                    self.lhs_mut().unwrap().beta_hno(limit, count);
294                    self.rhs_mut().unwrap().beta_hno(limit, count);
295                }
296            }
297            _ => (),
298        }
299    }
300}
301
302impl fmt::Display for Order {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        write!(
305            f,
306            "{}",
307            match *self {
308                NOR => "normal",
309                CBN => "call-by-name",
310                HSP => "head spine",
311                HNO => "hybrid normal",
312                APP => "applicative",
313                CBV => "call-by-value",
314                HAP => "hybrid applicative",
315            }
316        )
317    }
318}