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

Add a helper function for "passthrough" leapers #40

Merged
merged 2 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use crate::{
extend_with::ExtendWith,
filter_anti::FilterAnti,
filter_with::FilterWith,
filters::{PrefixFilter, ValueFilter},
filters::{passthrough, PrefixFilter, ValueFilter},
Leaper, Leapers, RelationLeaper,
},
variable::Variable,
Expand Down
47 changes: 47 additions & 0 deletions src/treefrog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,53 @@ pub(crate) mod filters {
}
}

pub struct Passthrough<Tuple> {
phantom: ::std::marker::PhantomData<Tuple>,
}

impl<Tuple> Passthrough<Tuple> {
fn new() -> Self {
Passthrough {
phantom: ::std::marker::PhantomData,
}
}
}

impl<'leap, Tuple> Leaper<'leap, Tuple, ()> for Passthrough<Tuple> {
/// Estimates the number of proposed values.
fn count(&mut self, _prefix: &Tuple) -> usize {
1
}
/// Populates `values` with proposed values.
fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap ()>) {
values.push(&())
}
/// Restricts `values` to proposed values.
fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap ()>) {
// `Passthrough` never removes values (although if we're here it indicates that the user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think we need a panic or an assertion here to notify users about that ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An assertion here will almost never trigger, even on programs that violate the rule in the parentheses. This leaper has a count of 1, and we always use the leaper with the lowest count to propose values. We return early (skipping propose and intersect) when the count is 0, so we only enter this function when:
- There's another leaper that proposes exactly one value.
- That leaper appears before us in the Leapers tuple (to break the tie).

Since it's not a hard error, just a small inefficiency, I think omitting it is fine. WDYT?

Copy link
Member

@lqd lqd Aug 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Thanks for the clarifications, I had missed this context. It doesn't look like an assertion would be useful in this situation indeed.

// didn't need a `Passthrough` in the first place)
}
}

/// Returns a leaper that proposes a single copy of each tuple from the main input.
///
/// Use this when you don't need any "extend" leapers in a join, only "filter"s. For example,
/// in the following datalog rule, all terms in the second and third predicate are bound in the
/// first one (the "main input" to our leapjoin).
///
/// ```prolog
/// error(loan, point) :-
/// origin_contains_loan_at(origin, loan, point), % main input
/// origin_live_at(origin, point),
/// loan_invalidated_at(loan, point).
/// ```
///
/// Without a passthrough leaper, neither the filter for `origin_live_at` nor the one for
/// `loan_invalidated_at` would propose any tuples, and the leapjoin would panic at runtime.
pub fn passthrough<Tuple>() -> Passthrough<Tuple> {
Passthrough::new()
}

/// A treefrog leaper based on a predicate of prefix and value.
/// Use like `ValueFilter::from(|tuple, value| ...)`. The closure
/// should return true if `value` ought to be retained. The
Expand Down