diff --git a/tamer/src/asg/graph/object/tpl.rs b/tamer/src/asg/graph/object/tpl.rs index 7585d42d..c8efedb8 100644 --- a/tamer/src/asg/graph/object/tpl.rs +++ b/tamer/src/asg/graph/object/tpl.rs @@ -50,26 +50,9 @@ impl Tpl { } } -impl Map for Tpl { - fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target { - match self { - Self(span, shape) => Self(f(span), shape), - } - } -} - -impl TryMap for Tpl { - fn try_map( - self, - f: impl FnOnce(TplShape) -> Self::FnResult, - ) -> Self::Result { - match self { - Self(span, x) => match f(x) { - Ok(shape) => Ok(Self(span, shape)), - Err((shape, e)) => Err((Self(span, shape), e)), - }, - } - } +impl_mono_map! { + Span => Tpl(@, shape), + TplShape => Tpl(span, @), } impl Display for Tpl { @@ -239,7 +222,7 @@ object_rel! { let span = to_oi.resolve(asg).span(); from_oi.try_map_obj(asg, |tpl| { - tpl.try_map(|shape| { + tpl.try_map(|shape: TplShape| { shape.try_adapt_to(TplShape::Expr(span), tpl_name) }) })?; @@ -269,7 +252,7 @@ object_rel! { // TODO: Refactor; very similar to Expr edge above. from_oi.try_map_obj(asg, |tpl| { - tpl.try_map(|shape| { + tpl.try_map(|shape: TplShape| { shape.try_adapt_to(apply_shape, tpl_name) }) })?; diff --git a/tamer/src/f.rs b/tamer/src/f.rs index 89d98f61..06e22db8 100644 --- a/tamer/src/f.rs +++ b/tamer/src/f.rs @@ -180,6 +180,80 @@ pub trait TryMap: Sized { } } +/// Generate monomorphic [`TryMap`] and [`Map`] implementations for the +/// provided type. +/// +/// This macro is suitable for otherwise-boilerplate `impl`s for these +/// traits. +/// If you expect anything more than a generic `map` or `try_map` operation, +/// then you should implement the traits manually. +/// +/// Only tuple structs are supported at present. +/// +/// For example: +/// +/// ``` +/// # #[macro_use] extern crate tamer; +/// # use tamer::impl_mono_map; +/// # use tamer::f::Map; +/// # fn main() { +/// #[derive(Debug, PartialEq)] +/// struct Foo(u8, Bar); +/// +/// #[derive(Debug, PartialEq)] +/// enum Bar { A, B }; +/// +/// impl_mono_map! { +/// u8 => Foo(@, bar), +/// Bar => Foo(n, @), +/// } +/// +/// assert_eq!(Foo(5, Bar::A).overwrite(Bar::B), Foo(5, Bar::B)); +/// # } +/// ``` +/// +/// Each line above generates a pair of `impl`s, +/// each for `Foo`, +/// where `@` represents the tuple item being mapped over. +#[macro_export] // for doc test above +macro_rules! impl_mono_map { + ($($t:ty => $tup:ident( $($pre:ident,)* @ $(, $post:ident),* ),)+) => { + $( + impl $crate::f::TryMap<$t> for $tup { + fn try_map( + self, + f: impl FnOnce($t) -> Self::FnResult, + ) -> Self::Result { + match self { + Self($($pre,)* x $(, $post),*) => match f(x) { + Ok(y) => Ok(Self($($pre,)* y $(, $post),*)), + Err((y, e)) => Err(( + Self($($pre,)* y $(, $post),*), + e, + )), + }, + } + } + } + + impl $crate::f::Map<$t> for $tup { + fn map(self, f: impl FnOnce($t) -> $t) -> Self::Target { + use std::convert::Infallible; + use $crate::f::TryMap; + + // `unwrap()` requires `E: Debug`, + // so this avoids that bound. + match self.try_map::(|x| Ok(f(x))) { + Ok(y) => y, + // Verbosely emphasize unreachability + Err::<_, (_, Infallible)>(_) => unreachable!(), + } + } + } + )+ + } +} + /// A nullary [`Fn`] delaying some computation. /// /// For the history and usage of this term in computing, diff --git a/tamer/src/lib.rs b/tamer/src/lib.rs index 5d8d7348..eeeae4bb 100644 --- a/tamer/src/lib.rs +++ b/tamer/src/lib.rs @@ -253,6 +253,8 @@ pub mod global; #[macro_use] extern crate static_assertions; +#[macro_use] +pub mod f; #[macro_use] pub mod diagnose; #[macro_use] @@ -260,7 +262,6 @@ pub mod xir; pub mod asg; pub mod convert; -pub mod f; pub mod fmt; pub mod fs; pub mod iter;