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

The value space of a domain. The inputs of every primitive and the result of every evaluation must be of this type.

If evaluation should fail, this would hold an appropriate error.

Required Methods

All inputs are thunks — argumentless functions — to get the underlying value.

Provided Methods

Implementors