Functors, Applicatives, And Monads In PicturesのApplicativeを、Rustで書いてみることにします。
RustでFunctor書いてみるの続きから書きたかったのですが、関数合成とか正直何を言っているのかサッパリ分からなかったので、Applicativeまですっ飛ばすことにします。
前回同様この記事の内容は正しくないかもしれません。どうかご了承くださいますようお願い致します。
##参考資料
Functors, Applicatives, And Monads In Pictures
箱で考えるFunctor、ApplicativeそしてMonad
Monads and GATs in nightly Rust
RustのHigher-Kinded type Trait
##Applicative
Just (+3) <*> Just 2 == Just 5
箱に入った値に、箱に入った関数を適応して、その結果を箱に入れて返すみたいな感じ
Just(2) -> Just(+3) -> Just(5)
> (*) <$> Just 5 <*> Just 3
Just 15
「(*) <$> Just 5」は「Just(*5)」と同じことなのかな?
> liftA2 (*) (Just 5) (Just 3)
Just 15
Applicativeだと二つの引数を取る関数を使える感じ?
よくわからないけど、とりあえず書いてみる
#![feature(generic_associated_types)]
#![feature(try_trait)]
trait Functor {
type Unwrapped;
type Wrapped<B>: Functor;
fn fmap<F, B>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> B;
}
trait Pointed: Functor {
fn wrap<T>(t: T) -> Self::Wrapped<T>;
}
trait Applicative: Pointed {
fn lift_a2<F, B, C>(self, b: Self::Wrapped<B>, f: F) -> Self::Wrapped<C>
where
F: FnOnce(Self::Unwrapped, B) -> C;
}
// Maybe
#[derive(Debug, PartialEq)]
enum Maybe<A> {
Just(A),
Nothing,
}
// ?部分がコンパイル通らなかったので、Optionを参考に持ってきた
impl<A> Maybe<A> {
pub fn ok_or<E>(self, err: E) -> Result<A, E> {
match self {
Maybe::Just(v) => Ok(v),
Maybe::Nothing => Err(err),
}
}
}
pub struct NoneError;
impl<A> std::ops::Try for Maybe<A> {
type Ok = A;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<A, NoneError> {
self.ok_or(NoneError)
}
#[inline]
fn from_ok(v: A) -> Self {
Maybe::Just(v)
}
#[inline]
fn from_error(_: NoneError) -> Self {
Maybe::Nothing
}
}
// 前回と同じ
impl<A> Functor for Maybe<A> {
type Unwrapped = A;
type Wrapped<B> = Maybe<B>;
fn fmap<F: FnOnce(A) -> B, B>(self, f: F) -> Maybe<B> {
match self {
Maybe::Just(x) => Maybe::Just(f(x)),
Maybe::Nothing => Maybe::Nothing,
}
}
}
// ここから Applicative
impl<A> Pointed for Maybe<A> {
fn wrap<T>(t: T) -> Maybe<T> {
Maybe::Just(t)
}
}
impl<A> Applicative for Maybe<A> {
fn lift_a2<F, B, C>(self, b: Self::Wrapped<B>, f: F) -> Self::Wrapped<C>
where
F: FnOnce(Self::Unwrapped, B) -> C,
{
let a = self?;
let b = b?;
Maybe::Just(f(a, b))
}
}
テストしてみる
// Just (+3) <*> Just 2 == Just 5
assert_eq!(
Maybe::Just(|v| 3 + v).lift_a2(Maybe::Just(2), |a, b| a(b)),
Maybe::Just(5)
);
// Just (*5) <*> Just 3 == Just 15
// (*) <$> Just 5 <*> Just 3 == Just 15
assert_eq!(
Maybe::Just(|v| 5 * v).lift_a2(Maybe::Just(3), |a, b| a(b)),
Maybe::Just(15)
);
// liftA2 (*) (Just 5) (Just 3) == Just 15
assert_eq!(
Maybe::Just(5).lift_a2(Maybe::Just(3), |a, b| a * b),
Maybe::Just(15)
);
ApplicativeはFunctorの別の書き方なのかな?
二つの引数の適応方法も引数で渡せるので色々応用が利くみたいな感じ?
##まとめ
「モナドは奥が深そう」というくらいは理解できたかもしれない、、、