tamer: f: impl_mono_map! macro

This helps to remove some boilerplate.  Testing this out in
`asg::graph::object::tpl` before applying it to other things; really `Map`
can just go away entirely then since it can be implemented in terms of
`TryMap`, but maybe it should stick around for manual impls (implementing
`TryMap` manually is more work).

DEV-13163
main
Mike Gerwitz 2023-07-27 02:35:47 -04:00
parent 3c9e1add20
commit 66512bf20d
3 changed files with 81 additions and 23 deletions

View File

@ -50,26 +50,9 @@ impl Tpl {
}
}
impl Map<Span> for Tpl {
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
match self {
Self(span, shape) => Self(f(span), shape),
}
}
}
impl TryMap<TplShape> for Tpl {
fn try_map<E>(
self,
f: impl FnOnce(TplShape) -> Self::FnResult<E>,
) -> Self::Result<E> {
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)
})
})?;

View File

@ -180,6 +180,80 @@ pub trait TryMap<T, U = T>: 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<E>(
self,
f: impl FnOnce($t) -> Self::FnResult<E>,
) -> Self::Result<E> {
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::<Infallible>(|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,

View File

@ -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;