From 98801455e4dc1f873b77fa0ddac6d26000ce6434 Mon Sep 17 00:00:00 2001 From: Aron Zwaan Date: Tue, 29 Oct 2024 10:25:50 +0100 Subject: [PATCH 1/8] Introduce trait for shadowing environment containers --- scopegraphs/src/containers/env.rs | 206 +++++++++++++++------------- scopegraphs/src/containers/path.rs | 11 +- scopegraphs/src/containers/scope.rs | 8 +- scopegraphs/src/resolve/lookup.rs | 57 ++------ scopegraphs/src/resolve/mod.rs | 4 +- scopegraphs/src/resolve/params.rs | 21 ++- 6 files changed, 149 insertions(+), 158 deletions(-) diff --git a/scopegraphs/src/containers/env.rs b/scopegraphs/src/containers/env.rs index 104c203..66b2ea6 100644 --- a/scopegraphs/src/containers/env.rs +++ b/scopegraphs/src/containers/env.rs @@ -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> + '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( @@ -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, @@ -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> 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, @@ -73,7 +55,6 @@ where } } -// Implementations for Results impl<'sg, LABEL: 'sg, DATA: 'sg, E> From> for Result, E> { @@ -83,23 +64,11 @@ impl<'sg, LABEL: 'sg, DATA: 'sg, E> From> } impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq, E: 'rslv> - EnvContainer<'sg, 'rslv, LABEL, DATA, bool> for Result, E> + EnvContainer<'sg, 'rslv, LABEL, DATA> for Result, 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), @@ -108,45 +77,22 @@ where } } -impl<'sg: 'rslv, 'rslv, LABEL: 'sg + Eq, DATA: 'sg + Eq, E: 'rslv> - EnvContainer<'sg, 'rslv, LABEL, DATA, Result> for Result, E> +impl<'sg: 'rslv, 'rslv, LABEL, DATA> From> + 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, 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, @@ -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> for Result, E> +where + ResolvedPath<'sg, LABEL, DATA>: Hash + Clone, + E: Clone, +{ + fn inject_if(data_ok: Result, 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>, @@ -183,25 +163,61 @@ where } }) } +} - fn flat_map( +// Mergeable + +/// Environment Container in which a path can be injected based on a condition. +pub trait Mergable<'sg, 'rslv, LABEL: 'sg, DATA: 'sg, DEQO>: + 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 merge_if( &self, - map: impl 'rslv + for<'short> FnOnce(&'short Env<'sg, LABEL, DATA>) -> Self, - ) -> Self { - let fut = Shared::clone(&self.0); - FutureWrapper::new(async move { - let env = fut.await; - map(&env).0.await - }) - } + data_equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = DEQO>, + sub_env: Self, + ) -> Self; } -impl<'sg: 'rslv, 'rslv, LABEL, DATA> From> - for FutureWrapper<'rslv, Env<'sg, LABEL, DATA>> +impl<'sg: 'rslv, 'rslv, LABEL: Eq + 'sg, DATA: Eq + 'sg, ENVC> + Mergable<'sg, 'rslv, LABEL, DATA, bool> for ENVC where - LABEL: Clone, + ENVC: EnvContainer<'sg, 'rslv, LABEL, DATA>, + 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 merge_if( + &self, + data_equiv: &'rslv impl DataEquivalence<'sg, DATA, Output = bool>, + sub_env: Self, + ) -> Self { + let base_env = self; + base_env.flat_map(move |base_env| { + if !base_env.is_empty() && data_equiv.always_equivalent() { + >>::from(base_env.clone()) + } else { + let base_env = base_env.clone(); + + sub_env.flat_map(move |sub_env| { + let filtered_env = sub_env + .iter() + .filter(|p1| { + base_env + .iter() + .find(|p2| data_equiv.data_equiv(p1.data, p2.data)) + .is_none() + }) + .collect::>(); + + let mut new_env = base_env; + for path in filtered_env { + new_env.insert(path.clone()) + } + new_env.into() + }) + } + }) } } diff --git a/scopegraphs/src/containers/path.rs b/scopegraphs/src/containers/path.rs index e3e5c7e..d92aab8 100644 --- a/scopegraphs/src/containers/path.rs +++ b/scopegraphs/src/containers/path.rs @@ -4,7 +4,7 @@ use futures::future::join_all; use std::fmt::Debug; use std::hash::Hash; -use super::EnvContainer; +use super::{Injectable, Mergable}; /// Interface for path containers that support the operations required for query resolution. pub trait PathContainer<'sg, 'rslv, LABEL: 'sg, DATA: 'sg>: Debug + 'rslv { @@ -21,9 +21,12 @@ 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>: 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> + + Mergable<'sg, 'rslv, LABEL, DATA, bool>; } impl<'sg, 'rslv, LABEL, DATA, DWFO, T> PathContainerWf<'sg, 'rslv, LABEL, DATA, DWFO> for T @@ -31,7 +34,9 @@ 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> + Mergable<'sg, 'rslv, LABEL, DATA, bool>, + ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone, { type EnvContainerWf = Self::EnvContainer; } diff --git a/scopegraphs/src/containers/scope.rs b/scopegraphs/src/containers/scope.rs index 3edca07..76c0c0c 100644 --- a/scopegraphs/src/containers/scope.rs +++ b/scopegraphs/src/containers/scope.rs @@ -1,9 +1,9 @@ use std::fmt::Debug; use std::hash::Hash; -use crate::future_wrapper::FutureWrapper; use crate::resolve::Path; use crate::Scope; +use crate::{future_wrapper::FutureWrapper, resolve::ResolvedPath}; use super::{PathContainer, PathContainerWf}; @@ -64,7 +64,7 @@ pub trait ScopeContainer<'sg, 'rslv, LABEL: Debug + 'sg, DATA: 'sg>: Debug { /// # use std::hash::Hash; /// /// -/// fn test<'sg, 'rslv, LABEL: Hash + Eq + Debug + 'sg, DATA: Hash + Eq + 'sg, DWFO>(cont: impl ScopeContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>) { +/// fn test<'sg, 'rslv, LABEL: Clone + Hash + Eq + Debug + 'sg, DATA: Hash + Eq + 'sg, DWFO>(cont: impl ScopeContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>) { /// /// } /// ``` @@ -78,7 +78,7 @@ pub trait ScopeContainer<'sg, 'rslv, LABEL: Debug + 'sg, DATA: 'sg>: Debug { /// test::<'_, '_, (), (), bool>(Result::<_, ()>::Ok(Vec::::new())); /// test::<'_, '_, (), (), Result>(Result::<_, ()>::Ok(Vec::::new())); /// -/// fn test<'sg, 'rslv, LABEL: Hash + Eq + Debug + 'sg, DATA: Hash + Eq + 'sg, DWFO>(cont: impl ScopeContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>) { +/// fn test<'sg, 'rslv, LABEL: Clone + Hash + Eq + Debug + 'sg, DATA: Hash + Eq + 'sg, DWFO>(cont: impl ScopeContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>) { /// /// } /// ``` @@ -88,6 +88,7 @@ pub trait ScopeContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>: where LABEL: Debug + 'sg, DATA: 'sg, + ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone, { /// Refinement of `Self::PathContainer`, carrying proof that this scope container resolves to valid path containers. type PathContainerWf: PathContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>; @@ -99,6 +100,7 @@ where DATA: 'sg, T: ScopeContainer<'sg, 'rslv, LABEL, DATA>, Self::PathContainer: PathContainerWf<'sg, 'rslv, LABEL, DATA, DWFO>, + ResolvedPath<'sg, LABEL, DATA>: Eq + Hash + Clone, { type PathContainerWf = Self::PathContainer; } diff --git a/scopegraphs/src/resolve/lookup.rs b/scopegraphs/src/resolve/lookup.rs index ee3a3f4..b1abc64 100644 --- a/scopegraphs/src/resolve/lookup.rs +++ b/scopegraphs/src/resolve/lookup.rs @@ -14,7 +14,8 @@ use std::sync::Arc; use crate::completeness::Completeness; use crate::containers::{ - EnvContainer, PathContainer, PathContainerWf, ScopeContainer, ScopeContainerWf, + EnvContainer, Injectable, Mergable, PathContainer, PathContainerWf, ScopeContainer, + ScopeContainerWf, }; use crate::resolve::{ DataEquivalence, DataWellformedness, EdgeOrData, Env, LabelOrder, Path, Query, Resolve, @@ -36,7 +37,7 @@ where // DWF : DataWellFormedNess LO: LabelOrder