Skip to content

Commit

Permalink
Merge pull request #63 from metaborg/equiv-containerized
Browse files Browse the repository at this point in the history
Ensure data equivalences can be delayed
  • Loading branch information
AZWN authored Oct 30, 2024
2 parents 3605d47 + 0f1b2ea commit 35b4e04
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 185 deletions.
3 changes: 3 additions & 0 deletions scopegraphs/examples/overload/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn main() {
println!("Hello from overload example!")
}
248 changes: 155 additions & 93 deletions scopegraphs/src/containers/env.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
use crate::future_wrapper::FutureWrapper;
use crate::resolve::{Env, ResolvedPath};
use crate::resolve::{DataEquivalence, Env, ResolvedPath};
use futures::future::Shared;
use std::hash::Hash;
use std::rc::Rc;

/// Interface for environment containers that support the operations required for query resolution.
pub trait EnvContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DWFO>:
pub trait EnvContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg>:
From<Env<'sg, LABEL, DATA>> + 'rslv
where
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
/// Creates a new, container with an empty environment.
fn empty() -> Self;
/// Creates a new container with an empty environment.
fn empty() -> Self {
Self::from(Env::new())
}

/// Creates a new environment that contains path when `data_ok` is `true`, and is empty otherwise.
fn inject_if(data_ok: DWFO, path: ResolvedPath<'sg, LABEL, DATA>) -> Self;
/// Creates a new container with a single path.
fn single(path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
Self::from(Env::single(path))
}

/// Maps the current container to a new one, based a provided mapping of the underlying environment.
fn flat_map(
Expand All @@ -21,23 +27,11 @@ pub trait EnvContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DWFO>:
) -> Self;
}

impl<'sg: 'rslv, 'rslv, LABEL: Eq, DATA: Eq> EnvContainer<'sg, 'rslv, LABEL, DATA, bool>
impl<'sg: 'rslv, 'rslv, LABEL: Eq, DATA: Eq> EnvContainer<'sg, 'rslv, LABEL, DATA>
for Env<'sg, LABEL, DATA>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
{
fn empty() -> Self {
Self::new()
}

fn inject_if(data_ok: bool, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
if data_ok {
Self::single(path)
} else {
Self::empty()
}
}

fn flat_map(
&self,
map: impl 'rslv + for<'short> FnOnce(&'short Env<'sg, LABEL, DATA>) -> Self,
Expand All @@ -46,25 +40,13 @@ where
}
}

impl<'sg: 'rslv, 'rslv, LABEL, DATA> EnvContainer<'sg, 'rslv, LABEL, DATA, bool>
impl<'sg: 'rslv, 'rslv, LABEL, DATA> EnvContainer<'sg, 'rslv, LABEL, DATA>
for Rc<Env<'sg, LABEL, DATA>>
where
ResolvedPath<'sg, LABEL, DATA>: Hash,
LABEL: 'sg + Eq + Clone,
DATA: 'sg + Eq,
{
fn empty() -> Self {
Self::new(Env::empty())
}

fn inject_if(data_ok: bool, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
if data_ok {
Env::single(path).into()
} else {
Self::empty()
}
}

fn flat_map(
&self,
map: impl for<'short> FnOnce(&'short Env<'sg, LABEL, DATA>) -> Self,
Expand All @@ -73,7 +55,6 @@ where
}
}

// Implementations for Results
impl<'sg, LABEL: 'sg, DATA: 'sg, E> From<Env<'sg, LABEL, DATA>>
for Result<Env<'sg, LABEL, DATA>, E>
{
Expand All @@ -83,23 +64,11 @@ impl<'sg, LABEL: 'sg, DATA: 'sg, E> From<Env<'sg, LABEL, DATA>>
}

impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq, E: 'rslv>
EnvContainer<'sg, 'rslv, LABEL, DATA, bool> for Result<Env<'sg, LABEL, DATA>, E>
EnvContainer<'sg, 'rslv, LABEL, DATA> for Result<Env<'sg, LABEL, DATA>, E>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
E: Clone,
{
fn empty() -> Self {
Ok(Env::empty())
}

fn inject_if(data_ok: bool, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
if data_ok {
Env::single(path).into()
} else {
Env::empty().into()
}
}

fn flat_map(&self, map: impl for<'short> FnOnce(&Env<'sg, LABEL, DATA>) -> Self) -> Self {
match self {
Ok(env) => map(env),
Expand All @@ -108,45 +77,22 @@ where
}
}

impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq, E: 'rslv>
EnvContainer<'sg, 'rslv, LABEL, DATA, Result<bool, E>> for Result<Env<'sg, LABEL, DATA>, E>
impl<'sg: 'rslv, 'rslv, LABEL, DATA> From<Env<'sg, LABEL, DATA>>
for FutureWrapper<'rslv, Env<'sg, LABEL, DATA>>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
E: Clone,
LABEL: Clone,
{
fn empty() -> Self {
Ok(Env::empty())
}

fn inject_if(data_ok: Result<bool, E>, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
data_ok.map(|ok| if ok { Env::single(path) } else { Env::empty() })
}

fn flat_map(&self, map: impl for<'short> FnOnce(&Env<'sg, LABEL, DATA>) -> Self) -> Self {
match self {
Ok(env) => map(env),
Err(err) => Err(err.clone()),
}
fn from(value: Env<'sg, LABEL, DATA>) -> Self {
FutureWrapper::new(std::future::ready(value))
}
}
impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq> EnvContainer<'sg, 'rslv, LABEL, DATA, bool>

impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq> EnvContainer<'sg, 'rslv, LABEL, DATA>
for FutureWrapper<'rslv, Env<'sg, LABEL, DATA>>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
LABEL: Clone,
{
fn empty() -> Self {
FutureWrapper::new(std::future::ready(Env::empty()))
}

fn inject_if(data_ok: bool, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
if data_ok {
Env::single(path).into()
} else {
Env::empty().into()
}
}

fn flat_map(
&self,
map: impl 'rslv + for<'short> FnOnce(&'short Env<'sg, LABEL, DATA>) -> Self,
Expand All @@ -159,17 +105,51 @@ where
}
}

// Injectable

/// Environment Container in which a path can be injected based on a condition.
pub trait Injectable<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DWFO>:
EnvContainer<'sg, 'rslv, LABEL, DATA>
where
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
/// Creates a new environment that contains path when `data_ok` is `true`, and is empty otherwise.
fn inject_if(data_ok: DWFO, path: ResolvedPath<'sg, LABEL, DATA>) -> Self;
}

impl<'sg: 'rslv, 'rslv, LABEL: Eq + 'sg, DATA: Eq + 'sg, ENVC>
Injectable<'sg, 'rslv, LABEL, DATA, bool> for ENVC
where
ENVC: EnvContainer<'sg, 'rslv, LABEL, DATA>,
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
fn inject_if(data_ok: bool, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
if data_ok {
Self::single(path)
} else {
Self::empty()
}
}
}

impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq, E: 'rslv>
Injectable<'sg, 'rslv, LABEL, DATA, Result<bool, E>> for Result<Env<'sg, LABEL, DATA>, E>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
E: Clone,
{
fn inject_if(data_ok: Result<bool, E>, path: ResolvedPath<'sg, LABEL, DATA>) -> Self {
data_ok.map(|ok| if ok { Env::single(path) } else { Env::empty() })
}
}

impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq>
EnvContainer<'sg, 'rslv, LABEL, DATA, FutureWrapper<'rslv, bool>>
Injectable<'sg, 'rslv, LABEL, DATA, FutureWrapper<'rslv, bool>>
for FutureWrapper<'rslv, Env<'sg, LABEL, DATA>>
where
ResolvedPath<'sg, LABEL, DATA>: Hash + Clone,
LABEL: Clone,
{
fn empty() -> Self {
FutureWrapper::new(std::future::ready(Env::empty()))
}

fn inject_if(
data_ok: FutureWrapper<'rslv, bool>,
path: ResolvedPath<'sg, LABEL, DATA>,
Expand All @@ -183,25 +163,107 @@ where
}
})
}
}

fn flat_map(
&self,
map: impl 'rslv + for<'short> FnOnce(&'short Env<'sg, LABEL, DATA>) -> Self,
// Filtering

/// Sub trait of [EnvContainer] that validates that filtering operations (for shadowing) can be applied on it.
pub trait Filterable<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DEQO>:
EnvContainer<'sg, 'rslv, LABEL, DATA>
where
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
/// Implementation of the filter operation on this container.
fn filter(
base_env: &Env<'sg, LABEL, DATA>,
sub_env: &Env<'sg, LABEL, DATA>,
equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = DEQO>,
) -> Self;
}

impl<'sg: 'rslv, 'rslv, LABEL: 'sg, DATA: 'sg, ENVC> Filterable<'sg, 'rslv, LABEL, DATA, bool>
for ENVC
where
ENVC: EnvContainer<'sg, 'rslv, LABEL, DATA>,
Env<'sg, LABEL, DATA>: Clone,
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
fn filter(
base_env: &Env<'sg, LABEL, DATA>,
sub_env: &Env<'sg, LABEL, DATA>,
equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = bool>,
) -> Self {
let fut = Shared::clone(&self.0);
FutureWrapper::new(async move {
let env = fut.await;
map(&env).0.await
})
sub_env
.iter()
.filter(|p1| !base_env.iter().any(|p2| equiv.data_equiv(p1.data, p2.data)))
.cloned()
.collect::<Env<_, _>>()
.into()
}
}

impl<'sg: 'rslv, 'rslv, LABEL, DATA> From<Env<'sg, LABEL, DATA>>
impl<'sg: 'rslv, 'rslv, LABEL: Clone + Eq + 'sg, DATA: Eq + 'sg, E: Clone + 'rslv>
Filterable<'sg, 'rslv, LABEL, DATA, Result<bool, E>> for Result<Env<'sg, LABEL, DATA>, E>
where
Env<'sg, LABEL, DATA>: Clone,
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
fn filter(
base_env: &Env<'sg, LABEL, DATA>,
sub_env: &Env<'sg, LABEL, DATA>,
equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = Result<bool, E>>,
) -> Self {
let sub_env = sub_env.clone();
sub_env.into_iter().try_fold(
Env::new(),
|mut filtered_env: Env<'sg, LABEL, DATA>, p1: ResolvedPath<'sg, LABEL, DATA>| {
let shadowed = base_env.iter().try_fold(
/* initially, not shadowed */ false,
|previously_shadowed: bool, p2: &ResolvedPath<'sg, LABEL, DATA>| {
if previously_shadowed {
Ok(true) // if it was shadowed, it will be
} else {
// not yet shadowed, try if current path shadows
equiv.data_equiv(p1.data, p2.data)
}
},
)?;
// p1 is not shadowed, so add it to accumulator
if !shadowed {
filtered_env.insert(p1);
}

Ok(filtered_env)
},
)
}
}

impl<'sg: 'rslv, 'rslv, LABEL: Clone + Eq + 'sg, DATA: Eq + 'sg>
Filterable<'sg, 'rslv, LABEL, DATA, FutureWrapper<'rslv, bool>>
for FutureWrapper<'rslv, Env<'sg, LABEL, DATA>>
where
LABEL: Clone,
Env<'sg, LABEL, DATA>: Clone,
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
fn from(value: Env<'sg, LABEL, DATA>) -> Self {
FutureWrapper::new(std::future::ready(value))
fn filter(
base_env: &Env<'sg, LABEL, DATA>,
sub_env: &Env<'sg, LABEL, DATA>,
equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = FutureWrapper<'rslv, bool>>,
) -> Self {
let base_env = base_env.clone();
let sub_env = sub_env.clone();
FutureWrapper::new(async move {
let mut filtered_env = Env::new();
'outer: for sub_path in sub_env {
for base_path in &base_env {
if equiv.data_equiv(sub_path.data, base_path.data).await {
continue 'outer;
}
}
filtered_env.insert(sub_path.clone());
}
filtered_env
})
}
}
16 changes: 11 additions & 5 deletions scopegraphs/src/containers/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use futures::future::join_all;
use std::fmt::Debug;
use std::hash::Hash;

use super::EnvContainer;
use super::{Filterable, Injectable};

/// Interface for path containers that support the operations required for query resolution.
pub trait PathContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg>: Debug + 'rslv {
Expand All @@ -19,19 +19,25 @@ pub trait PathContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg>: Debug + 'rslv {
}

/// Trait that is auto-implemented for any [PathContainer] implementation that yields a valid [EnvContainer].
pub trait PathContainerWf<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DWFO>:
pub trait PathContainerWf<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DWFO, DEQO>:
PathContainer<'sg, 'rslv, LABEL, DATA, EnvContainer = Self::EnvContainerWf>
where
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
/// Witness that ```Self::EnvContainer``` is a valid environment container.
type EnvContainerWf: EnvContainer<'sg, 'rslv, LABEL, DATA, DWFO>;
type EnvContainerWf: Injectable<'sg, 'rslv, LABEL, DATA, DWFO>
+ Filterable<'sg, 'rslv, LABEL, DATA, DEQO>;
}

impl<'sg, 'rslv, LABEL, DATA, DWFO, T> PathContainerWf<'sg, 'rslv, LABEL, DATA, DWFO> for T
impl<'sg, 'rslv, LABEL, DATA, DWFO, DEQO, T> PathContainerWf<'sg, 'rslv, LABEL, DATA, DWFO, DEQO>
for T
where
LABEL: Debug + 'sg,
DATA: 'sg,
T: PathContainer<'sg, 'rslv, LABEL, DATA>,
Self::EnvContainer: EnvContainer<'sg, 'rslv, LABEL, DATA, DWFO>,
Self::EnvContainer:
Injectable<'sg, 'rslv, LABEL, DATA, DWFO> + Filterable<'sg, 'rslv, LABEL, DATA, DEQO>,
ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone,
{
type EnvContainerWf = Self::EnvContainer;
}
Expand Down
Loading

0 comments on commit 35b4e04

Please sign in to comment.