pub struct State { /* private fields */ }
Expand description
The core struct used to contain and manage Value
bindings.
An open State can be updated in a few different ways. Most update methods
return an Option<State>
to reflect the fact each new constraint can
invalidate the state. This gives you the ability to quickly short circuit with the
?
operator
as soon the state hits a dead end.
A State
is designed to be cheap to clone()
, so make a copy if you want
to try multiple paths.
In general, it is most ergonomic to manipulate a state inside a function
that returns an Option<State>
to allow the use of the question mark
operator (Note that the .apply()
function makes it easy
to do this).
use canrun::{State, Value};
fn my_fn() -> Option<State> {
let x = Value::var();
let y = Value::var();
let state = State::new();
let maybe: Option<State> = state.unify(&x, &Value::new(1));
maybe?.unify(&x, &y)
}
assert!(my_fn().is_some());
Implementations§
source§impl State
impl State
sourcepub fn new() -> Self
pub fn new() -> Self
Create a new, empty state.
This often does not need to be used directly as you can
.query()
a Goal
directly, which handles the state creation internally.
However, there are use cases for creating and managing a state independently of any goals.
Example:
use canrun::{State};
let state = State::new();
sourcepub fn apply<F>(self, func: F) -> Option<Self>where
F: Fn(Self) -> Option<Self>,
pub fn apply<F>(self, func: F) -> Option<Self>where F: Fn(Self) -> Option<Self>,
Apply an arbitrary function to a state.
This is primarily a helper to make it easier to get into a function where you can use the question mark operator while applying multiple updates to a state.
Example:
use canrun::{State, Query, Value};
let state = State::new();
let x = Value::var();
let state = state.apply(|s| {
s.unify(&x, &Value::new(1))?
.unify(&Value::new(1), &x)
});
let results: Vec<_> = state.query(x).collect();
assert_eq!(results, vec![1]);
sourcepub fn resolve<T: Unify>(&self, val: &Value<T>) -> Value<T>
pub fn resolve<T: Unify>(&self, val: &Value<T>) -> Value<T>
Recursively resolve a Value
as far as the currently
known variable bindings allow.
This will return either the final Value::Resolved
(if found) or the
last Value::Var
it attempted to resolve. It will not force
forks
to enumerate all potential states, so potential
bindings that may eventually become confirmed are not considered. Use
StateIterator::into_states
if you want to attempt resolving against all (known) possible states.
Example:
use canrun::{State, Query, Value};
let state = State::new();
let x = Value::var();
assert_eq!(state.resolve(&x), x);
let state = state.unify(&x, &Value::new(1))?;
assert_eq!(state.resolve(&x), Value::new(1));
sourcepub fn unify<T: Unify>(self, a: &Value<T>, b: &Value<T>) -> Option<Self>
pub fn unify<T: Unify>(self, a: &Value<T>, b: &Value<T>) -> Option<Self>
Attempt to unify two values with each other.
If the unification fails, None
will be
returned. Value::Var
s will be checked against relevant
constraints, which can also cause a state to fail.
Examples:
use canrun::{State, Query, Value};
let x = Value::var();
let state = State::new();
let state = state.unify(&x, &Value::new(1));
assert!(state.is_some());
let state = State::new();
let state = state.unify(&Value::new(1), &Value::new(2));
assert!(state.is_none());
sourcepub fn constrain(self, constraint: Rc<dyn Constraint>) -> Option<Self>
pub fn constrain(self, constraint: Rc<dyn Constraint>) -> Option<Self>
Add a constraint to the store that can be reevaluated as variables are resolved.
Some logic is not easy or even possible to express until the resolved
values are available. .constrain()
provides a low level way to run
custom imperative code whenever certain bindings are updated.
See the Constraint
trait for more usage information.
sourcepub fn fork(self, fork: impl Fork) -> Option<Self>
pub fn fork(self, fork: impl Fork) -> Option<Self>
Add a potential fork point to the state.
If there are many possibilities for a certain value or set of values,
this method allows you to add a Fork
object that can enumerate those
possible alternate states.
While this is not quite as finicky as
Constraints
, you still probably want to use the
any
or either
goals.
Unification is performed eagerly as soon as it is
called. Constraints are run as variables are
resolved. Forking is executed lazily at the end, when
StateIterator::into_states
or .query()
is called.
sourcepub fn vars(&self) -> LVarList
pub fn vars(&self) -> LVarList
Generate a list of LVar
s in this state. This takes into account bound variables
and constraint watches.
sourcepub fn is_ready(&self) -> bool
pub fn is_ready(&self) -> bool
Returns true
if the State
has no open forks or constraints.
If ready, then a ReadyState
can be derived with State::ready()
.
sourcepub fn ready(self) -> Option<ReadyState>
pub fn ready(self) -> Option<ReadyState>
Returns a ReadyState
if the State
has no open forks or constraints.