Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negation #18

Merged
merged 6 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions canrun/src/collections/lmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ where
Vv: Unify + Reify<Reified = Vr>,
{
type Reified = HashMap<Kr, Vr>;
fn reify_in(&self, state: &State) -> Option<Self::Reified> {
fn reify_in(&self, state: &ReadyState) -> Option<Self::Reified> {
let LMap { map } = self;
let init = HashMap::with_capacity(map.len());
map.iter().try_fold(init, |mut map, (k, v)| {
Expand Down Expand Up @@ -178,7 +178,7 @@ macro_rules! lmap {
#[doc(inline)]
pub use lmap;

use crate::{Fork, Reify, State, StateIter, Unify, Value};
use crate::{Fork, ReadyState, Reify, State, StateIter, Unify, Value};

#[cfg(test)]
mod tests {
Expand Down
4 changes: 2 additions & 2 deletions canrun/src/collections/ltup.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*! A helper macro and some blanket implementations to facilitate working with tuples of [`Value`]s. */

use crate::core::{Reify, State, Unify, Value};
use crate::core::{ReadyState, Reify, State, Unify, Value};
use std::rc::Rc;

/** Create a tuple of [logical values](crate::Value) with automatic `Into<Value<T>>`
Expand Down Expand Up @@ -60,7 +60,7 @@ macro_rules! impl_reify_tuple {

impl<$($t: Reify< Reified = $r>, $r,)*> Reify for ($($t),*) {
type Reified = ($($t::Reified),*);
fn reify_in(&self, state: &State) -> Option<Self::Reified> {
fn reify_in(&self, state: &ReadyState) -> Option<Self::Reified> {
#![allow(non_snake_case)]
let ($($t),*) = self;
Some(($($t.reify_in(state)?),*))
Expand Down
6 changes: 3 additions & 3 deletions canrun/src/collections/lvec/get.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::LVec;
use crate::{
core::{
constraints::{resolve_2, Constraint, ResolveFn, VarWatch},
State, Unify, Value,
constraints::{resolve_2, Constraint, ResolveFn},
LVarList, State, Unify, Value,
},
goals::Goal,
};
Expand Down Expand Up @@ -69,7 +69,7 @@ impl<T: Unify> Goal for Get<T> {
}

impl<T: Unify> Constraint for Get<T> {
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
let (index, collection) = resolve_2(&self.index, &self.collection, state)?;
let item = self.item.clone();
let found = collection.vec.get(*index);
Expand Down
6 changes: 3 additions & 3 deletions canrun/src/collections/lvec/member.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::core::{
constraints::{resolve_1, Constraint, ResolveFn, VarWatch},
State, Unify, Value,
constraints::{resolve_1, Constraint, ResolveFn},
LVarList, State, Unify, Value,
};
use crate::goals::unify;
use crate::goals::Any;
Expand Down Expand Up @@ -78,7 +78,7 @@ impl<T: Unify> Clone for Member<T> {
}

impl<T: Unify> Constraint for Member<T> {
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
let collection = resolve_1(&self.collection, state)?;
let any = collection
.vec
Expand Down
7 changes: 5 additions & 2 deletions canrun/src/collections/lvec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ mod member;
mod slice;
mod subset;

use crate::core::{Reify, State, Unify, Value};
use crate::{
core::{Reify, State, Unify, Value},
ReadyState,
};
pub use get::{get, Get};
pub use member::{member, Member};
pub use slice::{slice, Slice};
Expand Down Expand Up @@ -65,7 +68,7 @@ impl<T: Unify> Unify for LVec<T> {

impl<T: Unify + Reify> Reify for LVec<T> {
type Reified = Vec<T::Reified>;
fn reify_in(&self, state: &State) -> Option<Vec<T::Reified>> {
fn reify_in(&self, state: &ReadyState) -> Option<Vec<T::Reified>> {
self.vec
.iter()
.map(|v: &Value<T>| v.reify_in(state))
Expand Down
6 changes: 3 additions & 3 deletions canrun/src/collections/lvec/slice.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::constraints::{resolve_2, Constraint, ResolveFn, VarWatch};
use crate::constraints::{resolve_2, Constraint, ResolveFn};
use crate::goals::{unify, Goal};
use crate::lvec::LVec;
use crate::{State, Unify, Value};
use crate::{LVarList, State, Unify, Value};
use std::fmt::Debug;
use std::ops::Range;
use std::rc::Rc;
Expand Down Expand Up @@ -68,7 +68,7 @@ impl<T: Unify> Constraint for Slice<T>
where
T: Unify,
{
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
let (range, collection) = resolve_2(&self.range, &self.collection, state)?;
let slice_a = self.slice.clone();
let slice_b = collection.vec.get((*range).clone());
Expand Down
6 changes: 3 additions & 3 deletions canrun/src/collections/lvec/subset.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::constraints::{resolve_2, Constraint, ResolveFn, VarWatch};
use crate::constraints::{resolve_2, Constraint, ResolveFn};
use crate::goals::{unify, Any, Goal};
use crate::lvec::LVec;
use crate::{State, Unify, Value};
use crate::{LVarList, State, Unify, Value};
use std::fmt::Debug;
use std::iter::repeat;
use std::rc::Rc;
Expand Down Expand Up @@ -64,7 +64,7 @@ impl<T: Unify> Goal for Subset<T> {
}

impl<T: Unify> Constraint for Subset<T> {
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
let (subset, collection) = resolve_2(&self.subset, &self.collection, state)?;

let col_size = collection.len();
Expand Down
76 changes: 29 additions & 47 deletions canrun/src/core/constraints.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
//! Run code when [`variables`](LVar) are resolved.
//! Run code when [`variables`](crate::LVar) are resolved.

use std::{fmt::Debug, rc::Rc};
use std::rc::Rc;

use super::{State, Unify};
use crate::core::{LVar, Value, Value::*, VarId};
use crate::{
core::{Value, Value::*},
LVarList,
};

/**
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.
/** Update a [`State`] whenever one or more [`crate::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
Returning a [`Err([LVarList])`](LVarList) signals that the constraint is not
satisfied. The constraint will be re-attempted when one of the specified
variables is bound to another value.

Expand All @@ -44,14 +26,14 @@ functions.

# NOTE:
The [`attempt`](Constraint::attempt) function must take care to [fully
resolve](State::resolve) any variables before creating a [`VarWatch`].
resolve](State::resolve) any variables before creating a [`LVarList`].
The [`resolve_1`], [`resolve_2`], [`OneOfTwo`] and [`TwoOfThree`]
helpers can simplify handling this (plus returning the [`VarWatch`]).
helpers can simplify handling this (plus returning the [`LVarList`]).

# Example:
```
use canrun::{State, Unify, Query, Value};
use canrun::constraints::{Constraint, resolve_1, ResolveFn, VarWatch};
use canrun::{State, Unify, Query, Value, LVarList};
use canrun::constraints::{Constraint, resolve_1, ResolveFn};
use std::rc::Rc;

struct Assert<T: Unify> {
Expand All @@ -61,7 +43,7 @@ struct Assert<T: Unify> {

impl<T: Unify> Constraint for Assert<T>
{
fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
let resolved = resolve_1(&self.val, state)?;
let assert = self.assert.clone();
Ok(Box::new(
Expand All @@ -87,35 +69,35 @@ assert_eq!(results, vec![2]);
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>;
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList>;
}

/// Resolve one [`Value`] or return an [`Err(VarWatch)`](VarWatch) in a
/// Resolve one [`Value`] or return an [`Err(LVarList)`](LVarList) in a
/// [`Constraint`].
pub fn resolve_1<A: Unify>(val: &Value<A>, state: &State) -> Result<Rc<A>, VarWatch> {
pub fn resolve_1<A: Unify>(val: &Value<A>, state: &State) -> Result<Rc<A>, LVarList> {
match state.resolve(val) {
Resolved(a) => Ok(a),
Var(var) => Err(VarWatch::one(var)),
Var(var) => Err(LVarList::one(var)),
}
}

/// Resolve two [`Value`]s or return an [`Err(VarWatch)`](VarWatch) in a
/// Resolve two [`Value`]s or return an [`Err(LVarList)`](LVarList) 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> {
) -> Result<(Rc<A>, Rc<B>), LVarList> {
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)),
(Var(var), _) => Err(LVarList::one(var)),
(_, Var(var)) => Err(LVarList::one(var)),
}
}

/// Resolve one out of two [`Value`]s or return an [`Err(VarWatch)`](VarWatch) in
/// Resolve one out of two [`Value`]s or return an [`Err(LVarList)`](LVarList) in
/// a [`Constraint`].
pub enum OneOfTwo<A: Unify, B: Unify> {
/// Returned when the first [`Value`] is successfully resolved.
Expand All @@ -126,18 +108,18 @@ pub enum OneOfTwo<A: Unify, B: Unify> {

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> {
pub fn resolve(a: &Value<A>, b: &Value<B>, state: &State) -> Result<OneOfTwo<A, B>, LVarList> {
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)),
(Var(a), Var(b)) => Err(LVarList::two(a, b)),
}
}
}

/// Resolve two out of three [`Value`]s or return an [`Err(VarWatch)`](VarWatch)
/// Resolve two out of three [`Value`]s or return an [`Err(LVarList)`](LVarList)
/// in a [`Constraint`].
pub enum TwoOfThree<A: Unify, B: Unify, C: Unify> {
/// Returned when the first and second [`Value`]s are successfully resolved.
Expand All @@ -155,17 +137,17 @@ impl<A: Unify, B: Unify, C: Unify> TwoOfThree<A, B, C> {
b: &Value<B>,
c: &Value<C>,
state: &State,
) -> Result<TwoOfThree<A, B, C>, VarWatch> {
) -> Result<TwoOfThree<A, B, C>, LVarList> {
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)),
(Var(a), Var(b), _) => Err(LVarList::two(a, b)),
(Var(a), _, Var(c)) => Err(LVarList::two(a, c)),
(_, Var(b), Var(c)) => Err(LVarList::two(b, c)),
}
}
}
65 changes: 65 additions & 0 deletions canrun/src/core/lvarlist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::fmt::Debug;

use itertools::Itertools;

use super::State;
use crate::{
core::{LVar, VarId},
resolve_any, AnyVal,
};

/**
An opaque list of untyped LVars.

This is usually used to set up a watch on behalf of a [`Constraint`](crate::constraints::Constraint).
Consider generating this with the [`resolve_1`](crate::constraints::resolve_1), [`resolve_2`](crate::constraints::resolve_2), [`OneOfTwo`](crate::constraints::OneOfTwo)
or [`TwoOfThree`](crate::constraints::TwoOfThree) helpers.

It is also the return value of [`State::vars()`].
*/
#[derive(Debug)]
pub struct LVarList(pub(crate) Vec<VarId>);

impl LVarList {
/// Create an `LVarList` from a single [`LVar`].
pub fn one<A>(a: LVar<A>) -> Self {
LVarList(vec![a.id])
}

/// Create an `LVarList` from two [`LVar`]s.
pub fn two<A, B>(a: LVar<A>, b: LVar<B>) -> Self {
LVarList(vec![a.id, b.id])
}

/// Generate a new `LVarList` based on `&self` with any variables that have
/// been been resolved in the passed in state removed.
pub fn without_resolved_in(&self, state: &State) -> LVarList {
LVarList(
self.0
.iter()
.filter_map(|id| {
if resolve_any(&state.values, &AnyVal::Var(*id)).is_resolved() {
None
} else {
Some(*id)
}
})
.collect(),
)
}

/// Returns the number of [`LVar`]s.
pub fn len(&self) -> usize {
self.0.len()
}

/// Returns true if the `LVarList` contains no elements.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Produce a single deduplicated `LVarList` from an [`Iterator`] of `LVarList`s.
pub fn flatten(lists: impl Iterator<Item = LVarList>) -> LVarList {
LVarList(lists.flat_map(|list| list.0.into_iter()).unique().collect())
}
}
12 changes: 11 additions & 1 deletion canrun/src/core/mkmvmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ impl<K: Eq + Hash + Clone + fmt::Debug, V: Clone> MKMVMap<K, V> {
// This attempts to be "correct" by cleaning up all of the ids
// when a value is extracted, but this does mean doing a fair
// amount of work every time. In theory we could one not bother
// and would only pay a minor cost skipping over the garbage.
// and would only pay a minor cost skipping over the garbage,
// except we have some other areas that depend on this being an
// accurate reflection of what is actually being watched.
self.keys = self.keys.alter(
|existing| {
let updated = existing?.without(&value.id);
Expand All @@ -78,6 +80,14 @@ impl<K: Eq + Hash + Clone + fmt::Debug, V: Clone> MKMVMap<K, V> {
}
Some(values)
}

pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}

pub fn keys(&self) -> impl Iterator<Item = &K> {
self.keys.keys()
}
}

impl<K: Eq + Hash + Clone + fmt::Debug, V> fmt::Debug for Value<K, V> {
Expand Down
Loading