1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
//! Run code when [`variables`](LVar) are resolved.
use std::{fmt::Debug, rc::Rc};
use super::{State, Unify};
use crate::core::{LVar, Value, Value::*, VarId};
/**
An alias for the function that should be returned by a successful
[`Constraint::attempt`] to update the [`State`].
*/
pub type ResolveFn = Box<dyn FnOnce(State) -> Option<State>>;
/**
A set of variables to watch on behalf of a [`Constraint`].
Consider generating this with the [`resolve_1`], [`resolve_2`], [`OneOfTwo`]
or [`TwoOfThree`] helpers.
*/
#[derive(Debug)]
pub struct VarWatch(pub(crate) Vec<VarId>);
impl VarWatch {
/// Watch one [`LVar`] for changes in a [`Constraint`].
pub fn one<A>(a: LVar<A>) -> Self {
VarWatch(vec![a.id])
}
/// Watch two [`LVar`]s for changes in a [`Constraint`].
pub fn two<A, B>(a: LVar<A>, b: LVar<B>) -> Self {
VarWatch(vec![a.id, b.id])
}
}
/** Update a [`State`] whenever one or more [`LVar`]s are resolved.
The [`Constraint::attempt`] function will be run when it is initially added.
Returning a [`Err([VarWatch])`](VarWatch) signals that the constraint is not
satisfied. The constraint will be re-attempted when one of the specified
variables is bound to another value.
You probably want to use the higher level [goal projection](crate::goals::project)
functions.
# NOTE:
The [`attempt`](Constraint::attempt) function must take care to [fully
resolve](State::resolve) any variables before creating a [`VarWatch`].
The [`resolve_1`], [`resolve_2`], [`OneOfTwo`] and [`TwoOfThree`]
helpers can simplify handling this (plus returning the [`VarWatch`]).
# Example:
```
use canrun::{State, Unify, Query, Value};
use canrun::constraints::{Constraint, resolve_1, ResolveFn, VarWatch};
use std::rc::Rc;
struct Assert<T: Unify> {
val: Value<T>,
assert: Rc<dyn Fn(&T) -> bool>,
}
impl<T: Unify> Constraint for Assert<T>
{
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
let resolved = resolve_1(&self.val, state)?;
let assert = self.assert.clone();
Ok(Box::new(
move |state: State| if assert(&*resolved) { Some(state) } else { None },
))
}
}
# fn test() -> Option<()> {
let x = Value::var();
let state = State::new();
let state = state.constrain(Rc::new(Assert {val: x.clone(), assert: Rc::new(|x| x > &1)}));
let state = state?.unify(&x, &Value::new(2));
let results: Vec<i32> = state.query(x).collect();
assert_eq!(results, vec![2]);
# Some(())
# }
# test();
```
*/
pub trait Constraint {
/// Resolve required variables in a state and resubscribe or request to
/// update the state.
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch>;
}
/// Resolve one [`Value`] or return an [`Err(VarWatch)`](VarWatch) in a
/// [`Constraint`].
pub fn resolve_1<A: Unify>(val: &Value<A>, state: &State) -> Result<Rc<A>, VarWatch> {
match state.resolve(val) {
Resolved(a) => Ok(a),
Var(var) => Err(VarWatch::one(var)),
}
}
/// Resolve two [`Value`]s or return an [`Err(VarWatch)`](VarWatch) in a
/// [`Constraint`].
pub fn resolve_2<A: Unify, B: Unify>(
a: &Value<A>,
b: &Value<B>,
state: &State,
) -> Result<(Rc<A>, Rc<B>), VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), Resolved(b)) => Ok((a, b)),
(Var(var), _) => Err(VarWatch::one(var)),
(_, Var(var)) => Err(VarWatch::one(var)),
}
}
/// Resolve one out of two [`Value`]s or return an [`Err(VarWatch)`](VarWatch) in
/// a [`Constraint`].
pub enum OneOfTwo<A: Unify, B: Unify> {
/// Returned when the first [`Value`] is successfully resolved.
A(Rc<A>, Value<B>),
/// Returned when the second [`Value`] is successfully resolved.
B(Value<A>, Rc<B>),
}
impl<A: Unify, B: Unify> OneOfTwo<A, B> {
/// Attempt to resolve a [`OneOfTwo`] enum from a [`State`].
pub fn resolve(a: &Value<A>, b: &Value<B>, state: &State) -> Result<OneOfTwo<A, B>, VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), b) => Ok(OneOfTwo::A(a, b)),
(a, Resolved(b)) => Ok(OneOfTwo::B(a, b)),
(Var(a), Var(b)) => Err(VarWatch::two(a, b)),
}
}
}
/// Resolve two out of three [`Value`]s or return an [`Err(VarWatch)`](VarWatch)
/// in a [`Constraint`].
pub enum TwoOfThree<A: Unify, B: Unify, C: Unify> {
/// Returned when the first and second [`Value`]s are successfully resolved.
AB(Rc<A>, Rc<B>, Value<C>),
/// Returned when the second and third [`Value`]s are successfully resolved.
BC(Value<A>, Rc<B>, Rc<C>),
/// Returned when the first and third [`Value`]s are successfully resolved.
AC(Rc<A>, Value<B>, Rc<C>),
}
impl<A: Unify, B: Unify, C: Unify> TwoOfThree<A, B, C> {
/// Attempt to resolve a [`TwoOfThree`] enum from a [`State`].
pub fn resolve(
a: &Value<A>,
b: &Value<B>,
c: &Value<C>,
state: &State,
) -> Result<TwoOfThree<A, B, C>, VarWatch> {
let a = state.resolve(a);
let b = state.resolve(b);
let c = state.resolve(c);
match (a, b, c) {
(Resolved(a), Resolved(b), c) => Ok(TwoOfThree::AB(a, b, c)),
(a, Resolved(b), Resolved(c)) => Ok(TwoOfThree::BC(a, b, c)),
(Resolved(a), b, Resolved(c)) => Ok(TwoOfThree::AC(a, b, c)),
(Var(a), Var(b), _) => Err(VarWatch::two(a, b)),
(Var(a), _, Var(c)) => Err(VarWatch::two(a, c)),
(_, Var(b), Var(c)) => Err(VarWatch::two(b, c)),
}
}
}