Trait programinduction::lambda::LazyEvaluator [−][src]
pub trait LazyEvaluator: Sized + Sync { type Space: Clone + PartialEq + Send + Sync; type Error: Clone + Sync; fn lazy_evaluate(
&self,
primitive: &str,
inps: &[LiftedLazyFunction<Self::Space, Self>]
) -> Result<Self::Space, Self::Error>; fn lift(
&self,
_f: LiftedLazyFunction<Self::Space, Self>
) -> Result<Self::Space, ()> { ... } }
Like Evaluator, but you get to decide whether certain arguments should be evaluated.
Use Language::lazy_eval to invoke.
This should look very much like an Evaluator, but rather than an evaluate method which
takes inputs in its Space, LazyEvaluator has a lazy_evaluate method which takes
inputs that are all thunks — or argumentless functions — which give a value in Space upon
evaluation via thunk.eval(&[])?.
The example below covers a domain where lazy evaluation is necessary. Consider a function that
takes a list which should return -1 if the list is empty, otherwise returning the first item in
the list. This function may look like (λ (if (empty? $0) -1 (car $0))). Without lazy
evaluation, both branches (-1 and (car $0)) are evaluated before the if primitive is
evaluated — this could fail when called on an empty list becuase (car $0) be evaluated. With
lazy evaluation, the if primitives is responsible for calling the evaluation of its
arguments.
Examples
use programinduction::lambda::{Language, LazyEvaluator, LiftedLazyFunction}; let dsl = Language::uniform(vec![ ("if", ptp!(0; @arrow[tp!(bool), tp!(0), tp!(0), tp!(0)])), ("empty?", ptp!(0; @arrow[tp!(list(tp!(0))), tp!(bool)])), ("car", ptp!(0; @arrow[tp!(list(tp!(0))), tp!(0)])), ("-1", ptp!(int)), ]); // note: we will only have lists of ints in the example. #[derive(Clone, Debug, PartialEq)] struct ListError(&'static str); #[derive(Clone, Debug, PartialEq)] enum ListSpace { Bool(bool), Num(i32), List(Vec<i32>), } use ListSpace::*; #[derive(Copy, Clone)] struct ListsEvaluator; impl LazyEvaluator for ListsEvaluator { type Space = ListSpace; type Error = ListError; fn lazy_evaluate( &self, primitive: &str, inps: &[LiftedLazyFunction<Self::Space, Self>], ) -> Result<Self::Space, Self::Error> { match primitive { "if" => match inps[0].eval(&[])? { Bool(true) => inps[1].eval(&[]), Bool(false) => inps[2].eval(&[]), _ => unreachable!(), }, "empty?" => match inps[0].eval(&[])? { List(xs) => Ok(Bool(xs.is_empty())), _ => unreachable!(), }, "car" => match inps[0].eval(&[])? { List(xs) => if !xs.is_empty() { Ok(Num(xs[0].clone())) } else { Err(ListError("cannot get car of empty list")) }, _ => unreachable!(), }, "-1" => Ok(Num(-1)), _ => unreachable!(), } } } // LazyEvaluator<Space = ListSpace, Error = ListError> let eval = ListsEvaluator; let expr = dsl.parse("(λ (if (empty? $0) -1 (car $0)))").unwrap(); let inps = vec![List(Vec::new())]; // without lazy evaluation, this would be an Err because of evaluation of (car $0) let evaluated = dsl.lazy_eval(&expr, eval, &inps); assert_eq!(evaluated, Ok(Num(-1)));
Associated Types
type Space: Clone + PartialEq + Send + Sync
The value space of a domain. The inputs of every primitive and the result of every evaluation must be of this type.
type Error: Clone + Sync
If evaluation should fail, this would hold an appropriate error.
Required Methods
fn lazy_evaluate(
&self,
primitive: &str,
inps: &[LiftedLazyFunction<Self::Space, Self>]
) -> Result<Self::Space, Self::Error>
&self,
primitive: &str,
inps: &[LiftedLazyFunction<Self::Space, Self>]
) -> Result<Self::Space, Self::Error>
All inputs are thunks — argumentless functions — to get the underlying value.