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

Get rid of the boxed iterator returned from query in favor of impl Iterator #24

Merged
merged 2 commits into from Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Expand Up @@ -31,7 +31,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.70.0
toolchain: 1.75.0
override: true
- name: Build
run: cargo build --verbose
Expand Down
2 changes: 1 addition & 1 deletion canrun/Cargo.toml
Expand Up @@ -10,7 +10,7 @@ categories = ["algorithms", "mathematics"]
keywords = ["logic", "dsl", "kanren"]
license = "MIT/Apache-2.0"
edition = "2021"
rust-version = "1.70.0"
rust-version = "1.75.0"

[dependencies]
im-rc = "15.1.0"
Expand Down
8 changes: 4 additions & 4 deletions canrun/src/core/query.rs
Expand Up @@ -24,7 +24,7 @@ A blanket impl covers anything that implements [`StateIterator`], so many
types including [`Goal`](crate::goals) and [`State`](crate::State) are
queryable.
*/
pub trait Query<'a> {
pub trait Query {
/**
Get [reified](crate::core::Reify) results from things that can produce
[`StateIter`](crate::core::StateIter)s.
Expand Down Expand Up @@ -57,11 +57,11 @@ pub trait Query<'a> {
assert_eq!(result, vec![1])
```
*/
fn query<Q: Reify + 'a>(self, query: Q) -> Box<dyn Iterator<Item = Q::Reified> + 'a>;
fn query<Q: Reify>(self, query: Q) -> impl Iterator<Item = Q::Reified>;
}

impl<'a, S: StateIterator + 'a> Query<'a> for S {
fn query<Q: Reify + 'a>(self, query: Q) -> Box<dyn Iterator<Item = Q::Reified> + 'a> {
impl<S: StateIterator> Query for S {
fn query<Q: Reify>(self, query: Q) -> impl Iterator<Item = Q::Reified> {
Box::new(
self.into_states()
.filter_map(move |s| query.reify_in(&s.ready()?)),
Expand Down
15 changes: 6 additions & 9 deletions canrun/src/goals/both.rs
Expand Up @@ -5,9 +5,9 @@ use crate::core::State;
A [Goal](crate::goals::Goal) that only succeeds if both sub-goals succeed. Create with [`both`].
*/
#[derive(Debug)]
pub struct Both {
a: Box<dyn Goal>,
b: Box<dyn Goal>,
pub struct Both<A: Goal, B: Goal> {
a: A,
b: B,
}

/**
Expand Down Expand Up @@ -41,14 +41,11 @@ let result: Vec<_> = goal.query(x).collect();
assert_eq!(result, vec![]) // Empty result
```
*/
pub fn both(a: impl Goal, b: impl Goal) -> Both {
Both {
a: Box::new(a),
b: Box::new(b),
}
pub fn both<A: Goal, B: Goal>(a: A, b: B) -> Both<A, B> {
Both { a, b }
}

impl Goal for Both {
impl<A: Goal, B: Goal> Goal for Both<A, B> {
fn apply(&self, state: State) -> Option<State> {
self.a.apply(state).and_then(|s| self.b.apply(s))
}
Expand Down
17 changes: 8 additions & 9 deletions canrun/src/goals/lazy.rs
Expand Up @@ -5,20 +5,18 @@ use crate::core::State;

use super::Goal;

type LazyFun = dyn Fn() -> Box<dyn Goal>;

/**
A [Goal](crate::goals::Goal) that is generated via callback just as
it is about to be evaluated. Create with [`lazy`].
*/
pub struct Lazy {
fun: Rc<LazyFun>,
pub struct Lazy<G: Goal> {
fun: Rc<dyn Fn() -> G>,
}

impl Debug for Lazy {
impl<G: Goal> Debug for Lazy<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Lazy")
.field("fun", &"Rc<dyn Fn() -> Box<dyn Goal>>")
.field("fun", &"Rc<dyn Fn() -> impl Goal>")
.finish()
}
}
Expand All @@ -44,14 +42,15 @@ let result: Vec<_> = goal.query(x).collect();
assert_eq!(result, vec![1])
```
*/
pub fn lazy<F>(fun: F) -> Lazy
pub fn lazy<F, G>(fun: F) -> Lazy<G>
where
F: (Fn() -> Box<dyn Goal>) + 'static,
G: Goal,
F: (Fn() -> G) + 'static,
{
Lazy { fun: Rc::new(fun) }
}

impl Goal for Lazy {
impl<G: Goal> Goal for Lazy<G> {
fn apply(&self, state: State) -> Option<State> {
let fun = &self.fun;
let goal = fun();
Expand Down
12 changes: 12 additions & 0 deletions canrun/src/goals/mod.rs
Expand Up @@ -82,3 +82,15 @@ impl Goal for Rc<dyn Goal> {
self.as_ref().apply(state)
}
}

impl<G: Goal> Goal for Rc<G> {
fn apply(&self, state: State) -> Option<State> {
self.as_ref().apply(state)
}
}

impl<G: Goal> Goal for Box<G> {
fn apply(&self, state: State) -> Option<State> {
self.as_ref().apply(state)
}
}
17 changes: 9 additions & 8 deletions canrun/src/goals/not.rs
Expand Up @@ -12,11 +12,11 @@ A [Goal](crate::goals::Goal) that only succeeds if the sub-goal is proved to alw
See [`not()`] for more details.
*/
#[derive(Debug)]
pub enum Not {
pub enum Not<G: Goal> {
/// A `Not` with a sub-goal that failed quickly at creation time
Fail,
/// A `Not` goal that needs further evaluation to see if it will succeed.
Maybe(Rc<NotConstraint>),
Maybe(Rc<NotConstraint<G>>),
}

/**
Expand Down Expand Up @@ -61,7 +61,7 @@ is able to conclusively prove or disprove the sub-goal.
All of this is not to discourage usage, but just to say that you should try to keep them
relatively simple and as precise as possible.
*/
pub fn not(goal: impl Goal) -> Not {
pub fn not<G: Goal>(goal: G) -> Not<G> {
// We run the subgoal in isolation right up front for two reasons...
let mut inner_states = goal.apply(State::new()).into_states().peekable();
if inner_states.peek().is_none() {
Expand All @@ -81,7 +81,7 @@ pub fn not(goal: impl Goal) -> Not {
}
}

impl Goal for Not {
impl<G: Goal> Goal for Not<G> {
fn apply(&self, state: State) -> Option<State> {
match self {
Not::Fail => Some(state),
Expand All @@ -96,18 +96,19 @@ impl Goal for Not {
}
}

/** A [`Not`] goal that needs to keep evaluating the state as variables are resolved. */
/** A [`Not`] goal that needs to keep evaluating the state as variables are
* resolved. */
#[derive(Debug)]
pub struct NotConstraint {
goal: Rc<dyn Goal>,
pub struct NotConstraint<G: Goal> {
goal: Rc<G>,
vars: LVarList,
}

fn any_succeed(state: Option<State>) -> bool {
state.into_states().next().is_some()
}

impl Constraint for NotConstraint {
impl<G: Goal> Constraint for NotConstraint<G> {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList> {
// If the internal goal succeeded...
if any_succeed(self.goal.apply(state.clone())) {
Expand Down
2 changes: 1 addition & 1 deletion examples/Cargo.toml
Expand Up @@ -7,7 +7,7 @@ repository = "https://github.com/tgecho/canrun_rs"
documentation = "https://docs.rs/crate/canrun_collections"
license = "MIT/Apache-2.0"
edition = "2021"
rust-version = "1.70.0"
rust-version = "1.75.0"

[dependencies]
canrun = { path = "../canrun"}
Expand Down