少し前にRustのnightlyにGATが入ったらしいので、Functors, Applicatives, And Monads In PicturesのFunctorまでを、Rustで書いてみることにしました。
Rustは趣味でコードを書いている程度で、Haskellは一行も書いたことがありません。それに、モナドが何なのかもサッパリ分かりません。なので、この記事の内容は正しくないかもしれません。どうかご了承くださいますようお願い致します。
あと、今回使用したRustのバージョンは、「rustc 1.51.0-nightly (0b644e419 2020-12-26)」になります。
##参考資料
Functors, Applicatives, And Monads In Pictures
箱で考えるFunctor、ApplicativeそしてMonad
Monads and GATs in nightly Rust
RustのHigher-Kinded type Trait
##Maybe
data Maybe a = Nothing | Just a
Maybe データ型、 Just a と Nothing の状態を表す
#[derive(Debug, PartialEq)]
enum Maybe<A> {
Just(A),
Nothing,
}
#Functor
> fmap (+3) (Just 2)
Just 5
> fmap (+3) Nothing
Nothing
箱に入った値に関数を適応して、その結果を箱に入れて返すみたいな感じ
Just(2) -> (+3) -> Just(5)
とりあえず書いてみる
impl<A> Maybe<A> {
pub fn fmap<B, F: FnOnce(A) -> B>(self, f: F) -> Maybe<B> {
match self {
Maybe::Just(x) => Maybe::Just(f(x)),
Maybe::Nothing => Maybe::Nothing,
}
}
}
テストしてみる
assert_eq!(Maybe::Just(2).fmap(|v| 3 + v), Maybe::Just(5));
assert_eq!(Maybe::Nothing.fmap(|v: i32| 3 + v), Maybe::Nothing);
これって Option
assert_eq!(Some(2).map(|v| 3 + v), Some(5));
assert_eq!(None.map(|v: i32| 3 + v), None);
Haskell の typeclass は、Rust の trait らしいので trait で書きなおす
class Functor f where
map :: (a -> b) -> f a -> f b
instance Functor Maybe where
fmap func (Just val) = Just (func val)
fmap func Nothing = Nothing
trait Functor<A> {
fn fmap<B, F: FnOnce(A) -> B>(self, f: F) -> Maybe<B>;
}
impl<A> Functor<A> for Maybe<A> {
fn fmap<B, F: FnOnce(A) -> B>(self, f: F) -> Maybe<B> {
match self {
Maybe::Just(x) => Maybe::Just(f(x)),
Maybe::Nothing => Maybe::Nothing,
}
}
}
あれ?書けた気がする?
##GAT
気を取り直して本題のGATで書いてみる
#![feature(generic_associated_types)]
を頭に追加して
trait Functor {
type Unwrapped;
type Wrapped<B>: Functor;
fn fmap<F, B>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> B;
}
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,
}
}
}
##HKT
実はGAT入る前から書けてた
RustにはHKTないけど、関連型を駆使して書けるらしい
trait HKT<U> {
type Unwrapped;
type Wrapped;
}
trait Functor<U>: HKT<U> {
fn fmap<F: FnOnce(Self::Unwrapped) -> U>(self, f: F) -> Self::Wrapped;
}
impl<A, U> HKT<U> for Maybe<A> {
type Unwrapped = A;
type Wrapped = Maybe<U>;
}
impl<A, U> Functor<U> for Maybe<A> {
fn fmap<F: FnOnce(Self::Unwrapped) -> U>(self, f: F) -> Self::Wrapped {
match self {
Maybe::Just(x) => Maybe::Just(f(x)),
Maybe::Nothing => Maybe::Nothing,
}
}
}
##まとめ
「type Wrapped<B>: Functor;
」な部分がGAT(Generic Associated Types)なところ
それにしてもモナドが何なのかサッパリわからん