tamer: f: Add TryMap
This implements TryMap and utilizes it in `asg::graph::object::tpl`. DEV-13163main
parent
38c0161257
commit
3c9e1add20
|
@ -22,7 +22,11 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::{prelude::*, Doc, Expr, Ident};
|
||||
use crate::{f::Map, parse::util::SPair, span::Span};
|
||||
use crate::{
|
||||
f::{Map, TryMap},
|
||||
parse::util::SPair,
|
||||
span::Span,
|
||||
};
|
||||
|
||||
/// Template with associated name.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -54,10 +58,16 @@ impl Map<Span> for Tpl {
|
|||
}
|
||||
}
|
||||
|
||||
impl Map<TplShape> for Tpl {
|
||||
fn map(self, f: impl FnOnce(TplShape) -> TplShape) -> Self::Target {
|
||||
impl TryMap<TplShape> for Tpl {
|
||||
fn try_map<E>(
|
||||
self,
|
||||
f: impl FnOnce(TplShape) -> Self::FnResult<E>,
|
||||
) -> Self::Result<E> {
|
||||
match self {
|
||||
Self(span, shape) => Self(span, f(shape)),
|
||||
Self(span, x) => match f(x) {
|
||||
Ok(shape) => Ok(Self(span, shape)),
|
||||
Err((shape, e)) => Err((Self(span, shape), e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,15 +161,22 @@ pub enum TplShape {
|
|||
}
|
||||
|
||||
impl TplShape {
|
||||
/// Attempt to adapt a template shape to that of another.
|
||||
///
|
||||
/// If the shape of `other` is a refinement of the shape of `self`,
|
||||
/// then `other` will be chosen.
|
||||
/// If the shape of `other` conflicts with `self`,
|
||||
/// an appropriate [`AsgError`] will describe the problem.
|
||||
fn try_adapt_to(
|
||||
self,
|
||||
other: TplShape,
|
||||
tpl_name: Option<SPair>,
|
||||
) -> Result<Self, AsgError> {
|
||||
) -> Result<Self, (Self, AsgError)> {
|
||||
match (self, other) {
|
||||
(TplShape::Expr(first_span), TplShape::Expr(bad_span)) => {
|
||||
Err(AsgError::TplShapeExprMulti(tpl_name, bad_span, first_span))
|
||||
}
|
||||
(TplShape::Expr(first_span), TplShape::Expr(bad_span)) => Err((
|
||||
self,
|
||||
AsgError::TplShapeExprMulti(tpl_name, bad_span, first_span),
|
||||
)),
|
||||
|
||||
// Higher levels of specificity take precedence.
|
||||
(shape @ TplShape::Expr(_), TplShape::Empty)
|
||||
|
@ -177,6 +194,14 @@ impl TplShape {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the shape stores [`Span`] information as evidence of inference,
|
||||
/// overwrite it with the provided `span`.
|
||||
///
|
||||
/// This is most commonly used to encapsulate a previous shape
|
||||
/// inference.
|
||||
/// For example,
|
||||
/// a template application's span may overwrite the inferred shape of
|
||||
/// its own body.
|
||||
fn overwrite_span_if_any(self, span: Span) -> Self {
|
||||
match self {
|
||||
TplShape::Empty | TplShape::Unknown => self,
|
||||
|
@ -214,14 +239,9 @@ object_rel! {
|
|||
let span = to_oi.resolve(asg).span();
|
||||
|
||||
from_oi.try_map_obj(asg, |tpl| {
|
||||
let new = tpl
|
||||
.shape()
|
||||
.try_adapt_to(TplShape::Expr(span), tpl_name);
|
||||
|
||||
match new {
|
||||
Ok(shape) => Ok(tpl.overwrite(shape)),
|
||||
Err(e) => Err((tpl, e)),
|
||||
}
|
||||
tpl.try_map(|shape| {
|
||||
shape.try_adapt_to(TplShape::Expr(span), tpl_name)
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(commit(asg))
|
||||
|
@ -249,14 +269,9 @@ object_rel! {
|
|||
|
||||
// TODO: Refactor; very similar to Expr edge above.
|
||||
from_oi.try_map_obj(asg, |tpl| {
|
||||
let new = tpl
|
||||
.shape()
|
||||
.try_adapt_to(apply_shape, tpl_name);
|
||||
|
||||
match new {
|
||||
Ok(shape) => Ok(tpl.overwrite(shape)),
|
||||
Err(e) => Err((tpl, e)),
|
||||
}
|
||||
tpl.try_map(|shape| {
|
||||
shape.try_adapt_to(apply_shape, tpl_name)
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(commit(asg))
|
||||
|
|
|
@ -51,6 +51,9 @@
|
|||
/// This trait also provides a number of convenience methods that can be
|
||||
/// implemented in terms of [`Map::map`].
|
||||
///
|
||||
/// If a mapping can fail,
|
||||
/// see [`TryMap`].
|
||||
///
|
||||
/// Why a primitive `map` instead of `fmap`?
|
||||
/// ========================================
|
||||
/// One of the methods of this trait is [`Map::fmap`],
|
||||
|
@ -124,6 +127,59 @@ impl<T, U> Map<T, U> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type providing a `try_map` function from inner type `T` to `U`.
|
||||
///
|
||||
/// This is a fallible version of [`Map`];
|
||||
/// see that trait for more information.
|
||||
pub trait TryMap<T, U = T>: Sized {
|
||||
/// Type of object resulting from [`TryMap::try_map`] operation.
|
||||
///
|
||||
/// The term "target" originates from category theory,
|
||||
/// representing the codomain of the functor.
|
||||
type Target = Self;
|
||||
|
||||
/// Result of the mapping function.
|
||||
type FnResult<E> = Result<T, (T, E)>;
|
||||
|
||||
/// Result of the entire map operation.
|
||||
type Result<E> = Result<Self, (Self, E)>;
|
||||
|
||||
/// A structure-preserving map between types `T` and `U`.
|
||||
///
|
||||
/// This unwraps any number of `T` from `Self` and applies the
|
||||
/// function `f` to transform it into `U`,
|
||||
/// wrapping the result back up into [`Self`].
|
||||
///
|
||||
/// Since this method takes ownership over `self` rather than a mutable
|
||||
/// reference,
|
||||
/// [`Self::FnResult`] is expected to return some version of `T`
|
||||
/// alongside the error `E`;
|
||||
/// this is usually the original `self`,
|
||||
/// but does not have to be.
|
||||
/// Similarly,
|
||||
/// [`Self::Result`] will also return [`Self`] in the event of an
|
||||
/// error.
|
||||
///
|
||||
/// This is the only method that needs to be implemented on this trait;
|
||||
/// all others are implemented in terms of it.
|
||||
fn try_map<E>(
|
||||
self,
|
||||
f: impl FnOnce(T) -> Self::FnResult<E>,
|
||||
) -> Self::Result<E>;
|
||||
|
||||
/// Curried form of [`TryMap::try_map`],
|
||||
/// with arguments reversed.
|
||||
///
|
||||
/// `try_fmap` returns a unary closure that accepts an object of type
|
||||
/// [`Self`].
|
||||
/// This is more amenable to function composition and a point-free style.
|
||||
fn try_fmap<E>(
|
||||
f: impl FnOnce(T) -> Self::FnResult<E>,
|
||||
) -> impl FnOnce(Self) -> Self::Result<E> {
|
||||
move |x| x.try_map(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A nullary [`Fn`] delaying some computation.
|
||||
///
|
||||
/// For the history and usage of this term in computing,
|
||||
|
|
Loading…
Reference in New Issue