use super::functor::Functor;
use super::*;
use super::types::*;
pub trait Applicative: Functor {
fn pure<T>(t: T) -> K1<Self, T>;
fn app<A: Clone, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B>;
}
impl Applicative for OptionC {
fn pure<T>(t: T) -> K1<Self, T> {
OptionC::new(Some(t))
}
fn app<A, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B> {
OptionC::new(
f.into_inner()
.and_then(move |f| a.into_inner().map(move |a| f(a))),
)
}
}
impl Applicative for VecC {
fn pure<T>(t: T) -> K1<Self, T> {
VecC::new(vec![t])
}
fn app<A: Clone, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B> {
VecC::new(
f.into_inner()
.into_iter()
.flat_map(move |f| {
let f = &f;
a.inner()
.into_iter()
.map(move |a| f(a.clone()))
.collect::<Vec<_>>()
})
.collect()
)
}
}
#[cfg(test)]
mod test {
use super::*;
fn check_law_id<T: Clone, F: Applicative + Kind1<T>>(input: K1<F, T>)
where
K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
{
let id = F::pure(|x: T| x);
let output = F::app(id, input.clone());
assert_eq!(input, output);
}
fn check_law_homomorphism<F: Applicative + Kind1<T>, T: Clone, G>(g: G, input: T)
where
G: Fn(T) -> T + Clone,
K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
{
let pure_g = F::pure(g.clone());
let pure_x = F::pure(input.clone());
let pure_first = F::app(pure_g, pure_x);
let app_first = F::pure(g(input));
assert_eq!(pure_first, app_first);
}
fn check_law_interchange<F: Applicative + Kind1<T> + Kind1<G>, T: Clone, G>(
u: K1<F, G>,
input: T,
) where
G: Fn(T) -> T + Clone,
K1Type<F, G>: Clone,
K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
{
fn call_with<A: Clone, B, F: Fn(A) -> B>(a: A) -> impl Fn(F) -> B {
move |f| f(a.clone())
}
let pure_y = F::pure(input.clone());
let call_me = F::pure(call_with(input));
let u_first = F::app(u.clone(), pure_y);
let y_first = F::app(call_me, u);
assert_eq!(u_first, y_first);
}
fn check_law_composition<U, V, W, F: Applicative, X, Y>(u: K1<F, U>, v: K1<F, V>, w: K1<F, W>)
where
W: Clone,
X: Clone,
Y: Clone,
F: Kind1<U> + Kind1<V> + Kind1<W> + Kind1<Y>,
U: Fn(X) -> Y + Clone,
K1Type<F, U>: Clone,
V: Fn(W) -> X + Clone,
K1Type<F, V>: Clone,
K1Type<F, W>: Clone,
K1Type<F, Y>: PartialEq + Clone + core::fmt::Debug,
{
let compose = |f: U| {
move |g: V| {
let f = f.clone();
move |a| (f.clone())((g.clone())(a))
}
};
let nested_right = F::app(u.clone(), F::app(v.clone(), w.clone()));
let nested_left = F::app(F::app(F::app(F::pure(compose), u), v), w);
assert_eq!(nested_right, nested_left);
}
#[test]
fn option_applicative_law_id() {
check_law_id::<i32, _>(OptionC::new(None));
check_law_id(OptionC::new(Some(42)));
}
#[test]
fn option_applicative_law_homomorphism() {
let g = |x| x + 1;
check_law_homomorphism::<OptionC, _, _>(&g, 42);
}
#[test]
fn option_applicative_law_interchange() {
let g = OptionC::pure(|x| x + 1);
let no_g: K1<OptionC, fn(i32) -> i32> = OptionC::new(None);
check_law_interchange(g, 42);
check_law_interchange(no_g, 42);
}
#[test]
fn option_applicative_law_composition() {
check_law_composition(
OptionC::new(Some(|x| x * 2)),
OptionC::new(Some(|x| x + 1)),
OptionC::new(Some(42)),
);
check_law_composition::<_, _, i32, _, _, _>(
OptionC::new(Some(|x| x * 2)),
OptionC::new(Some(|x| x + 1)),
OptionC::new(None),
);
check_law_composition::<_, fn(i32) -> i32, _, _, _, _>(
OptionC::new(Some(|x| x * 2)),
OptionC::new(None),
OptionC::new(Some(42)),
);
check_law_composition::<fn(i32) -> i32, _, _, _, _, _>(
OptionC::new(None),
OptionC::new(Some(|x| x + 1)),
OptionC::new(Some(42)),
);
}
#[test]
fn vec_applicative_law_id() {
check_law_id::<i32, _>(VecC::new(vec![]));
check_law_id(VecC::new(vec![42]));
check_law_id(VecC::new(vec![1, 2, 3]));
}
#[test]
fn vec_applicative_law_homomorphism() {
let g = |x| x + 1;
check_law_homomorphism::<VecC, _, _>(&g, 42);
}
#[test]
fn vec_applicative_law_interchange() {
let g = VecC::pure(|x| x + 1);
let no_g: K1<VecC, fn(i32) -> i32> = VecC::new(vec![]);
check_law_interchange(g, 42);
check_law_interchange(no_g, 42);
}
#[test]
fn vec_applicative_law_composition() {
check_law_composition(
VecC::new(vec![|x| x * 2]),
VecC::new(vec![|x| x + 1]),
VecC::new(vec![42]),
);
check_law_composition::<_, _, i32, _, _, _>(
VecC::new(vec![|x| x * 2]),
VecC::new(vec![|x| x + 1]),
VecC::new(vec![]),
);
check_law_composition::<_, fn(i32) -> i32, _, _, _, _>(
VecC::new(vec![|x| x * 2]),
VecC::new(vec![]),
VecC::new(vec![42]),
);
check_law_composition::<fn(i32) -> i32, _, _, _, _, _>(
VecC::new(vec![]),
VecC::new(vec![|x| x + 1]),
VecC::new(vec![42]),
);
}
}