LoginSignup
14

More than 1 year has passed since last update.

posted at

updated at

RustでFunctor書いてみる

少し前に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

Haskell
data Maybe a = Nothing | Just a

Maybe データ型、 Just a と Nothing の状態を表す

Rust
#[derive(Debug, PartialEq)]
enum Maybe<A> {
    Just(A),
    Nothing,
}

Functor

Haskell
> fmap (+3) (Just 2)
Just 5

> fmap (+3) Nothing
Nothing

箱に入った値に関数を適応して、その結果を箱に入れて返すみたいな感じ

Just(2) -> (+3) -> Just(5)

とりあえず書いてみる

Rust
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,
        }
    }
}

テストしてみる

#[test]
assert_eq!(Maybe::Just(2).fmap(|v| 3 + v), Maybe::Just(5));
assert_eq!(Maybe::Nothing.fmap(|v: i32| 3 + v), Maybe::Nothing);

これって Option

#[test]
assert_eq!(Some(2).map(|v| 3 + v), Some(5));
assert_eq!(None.map(|v: i32| 3 + v), None);

Haskell の typeclass は、Rust の trait らしいので trait で書きなおす

Haskell
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
Rust
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)]

を頭に追加して

Rust
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ないけど、関連型を駆使して書けるらしい

Rust
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)なところ

それにしてもモナドが何なのかサッパリわからん

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
14