use crate::typeclasses::hkt::{FunctorHKT, HKT};
pub trait TransformatioNaturalis<F: HKT, G: HKT> {
fn transforma<A>(fa: F::Target<A>) -> G::Target<A>;
}
pub struct TransformatioFn<F: HKT, G: HKT, Func> {
f: Func,
_f: core::marker::PhantomData<F>,
_g: core::marker::PhantomData<G>,
}
impl<F: HKT, G: HKT, Func> TransformatioFn<F, G, Func> {
#[inline]
pub fn new(f: Func) -> Self {
TransformatioFn {
f,
_f: core::marker::PhantomData,
_g: core::marker::PhantomData,
}
}
#[inline]
pub fn apply<A>(&self, fa: F::Target<A>) -> G::Target<A>
where
Func: Fn(F::Target<A>) -> G::Target<A>,
{
(self.f)(fa)
}
}
pub struct TransformatioIdentitas<F>(core::marker::PhantomData<F>);
impl<F> Default for TransformatioIdentitas<F> {
#[inline]
fn default() -> Self {
TransformatioIdentitas(core::marker::PhantomData)
}
}
impl<F: HKT> TransformatioNaturalis<F, F> for TransformatioIdentitas<F> {
#[inline]
fn transforma<A>(fa: F::Target<A>) -> F::Target<A> {
fa
}
}
pub struct TransformatioCompositio<F, G, H, Eta, Theta> {
_eta: core::marker::PhantomData<Eta>,
_theta: core::marker::PhantomData<Theta>,
_f: core::marker::PhantomData<F>,
_g: core::marker::PhantomData<G>,
_h: core::marker::PhantomData<H>,
}
impl<F: HKT, G: HKT, H: HKT, Eta, Theta> TransformatioNaturalis<F, H>
for TransformatioCompositio<F, G, H, Eta, Theta>
where
Eta: TransformatioNaturalis<F, G>,
Theta: TransformatioNaturalis<G, H>,
{
#[inline]
fn transforma<A>(fa: F::Target<A>) -> H::Target<A> {
let ga: G::Target<A> = Eta::transforma(fa);
Theta::transforma(ga)
}
}
#[derive(Clone)]
pub struct OptionFWitness;
impl HKT for OptionFWitness {
type Target<T> = Option<T>;
}
impl FunctorHKT for OptionFWitness {
#[inline]
fn map<A, B, F>(fa: Option<A>, f: F) -> Option<B>
where
F: FnMut(A) -> B,
{
fa.map(f)
}
}
pub struct ResultFWitness<E>(core::marker::PhantomData<E>);
impl<E> HKT for ResultFWitness<E> {
type Target<T> = Result<T, E>;
}
impl<E: Clone> FunctorHKT for ResultFWitness<E> {
#[inline]
fn map<A, B, F>(fa: Result<A, E>, f: F) -> Result<B, E>
where
F: FnMut(A) -> B,
{
fa.map(f)
}
}
pub struct IdentitasFWitness;
impl HKT for IdentitasFWitness {
type Target<T> = T;
}
impl FunctorHKT for IdentitasFWitness {
#[inline]
fn map<A, B, F>(fa: A, mut f: F) -> B
where
F: FnMut(A) -> B,
{
f(fa)
}
}
#[cfg(feature = "alloc")]
pub struct ConstFWitness<C>(core::marker::PhantomData<C>);
#[cfg(feature = "alloc")]
impl<C> HKT for ConstFWitness<C> {
type Target<T> = C;
}
#[cfg(test)]
mod tests {
use super::*;
struct OptionIdentity;
impl TransformatioNaturalis<OptionFWitness, OptionFWitness> for OptionIdentity {
fn transforma<A>(fa: Option<A>) -> Option<A> {
fa
}
}
#[test]
fn test_identity_transformation() {
let opt = Some(42);
let result: Option<i32> = TransformatioIdentitas::<OptionFWitness>::transforma(opt);
assert_eq!(result, Some(42));
let by_example: Option<i32> = OptionIdentity::transforma(Some(42));
assert_eq!(by_example, Some(42));
}
#[test]
fn test_identity_on_none() {
let opt: Option<i32> = None;
let result: Option<i32> = TransformatioIdentitas::<OptionFWitness>::transforma(opt);
assert_eq!(result, None);
}
#[test]
fn test_result_witness() {
let ok: Result<i32, &str> = Ok(42);
let mapped: Result<i32, &str> = ResultFWitness::<&str>::map(ok, |x| x * 2);
assert_eq!(mapped, Ok(84));
}
#[test]
fn test_identity_witness() {
let value = 42;
let mapped = IdentitasFWitness::map(value, |x| x * 2);
assert_eq!(mapped, 84);
}
#[test]
fn test_composition_of_natural_transformations() {
type Id = TransformatioIdentitas<OptionFWitness>;
type Composed =
TransformatioCompositio<OptionFWitness, OptionFWitness, OptionFWitness, Id, Id>;
let result: Option<i32> = <Composed as TransformatioNaturalis<
OptionFWitness,
OptionFWitness,
>>::transforma(Some(7));
assert_eq!(
result,
Some(7),
"composed identity must preserve Some value"
);
let result: Option<i32> =
<Composed as TransformatioNaturalis<OptionFWitness, OptionFWitness>>::transforma(None);
assert_eq!(result, None, "composed identity must preserve None");
}
}