Functor, Applicative, Monad

TL;DR

冬休みの宿題として、関数型言語の基礎を勉強しようとしてなんとなくしか理解できてなかった、Monad とか Functor とかその辺を勉強した。
ずいぶん長いこと積んでた、プログラマのための圏論の上中下を読んだり、Haskellに入門したりした

勉強の方法

目的は、Functor とか Monad らへんの知識を勉強することだった。
振り返ってみると以下のように勉強すると良いように思う。

まず、 Functors, Applicatives, And Monads In Pictures を読む。
Functor くらいまでは何を言ってるかわかるが、 Haskell で説明コードが書かれてることもあり段々わからなくなるので、Learn You a Haskell for Great Good! を読みつつ Haskell の環境構築をして書いてみたりする。

個人的には、 :t という型を出力するコマンドをめちゃくちゃ多用したので頭の片隅に入れておくと良さそう。

そのあと、Learn You a Haskell for Great Good! - Making Our Own Types and Typeclasses を読んで Functor を理解する。
引き続き Learn You a Haskell for Great Good! - Functors, Applicative Functors and Monoids を読んで Applicative と Monoid を理解する。
最後に Learn You a Haskell for Great Good! - A Fistful of Monads を読んで Functors, Applicatives, And Monads In Pictures を再度読んでみると不思議と概念を理解した感じがする。

そして余裕があれば、以下のもっと理論的な圏論の背景などを読むとよい (自分はこれを最初に読んで時間がかかった感覚がなんとなくある) - プログラマーのための圏論(上) - プログラマーのための圏論(中) - プログラマーのための圏論(下)

自分用のまとめでもあるメモ

Functor

Learn You a Haskell for Great Good! - Making Our Own Types and Typeclasses#The Functor typeclass

class Functor f where  
    fmap :: (a -> b) -> f a -> f b  

引数が、 (a -> b)f a (type parameter a を 持つ f) で returnが f b (type parameter b を 持つ f)
例えば、配列の操作をするmapは

map :: (a -> b) -> [a] -> [b]

であり、

instance Functor [] where  
    fmap = map  

[] は Int などの type parameter を取る型なので条件を満たしてる ( [a]f a)

なので、 map は 配列のための fmap と言える。

Functorは、以下の規則を守る法則を守る必要があり、Haskell だとこれを守るのはユーザーとなってる > 1. if we map the id function over a functor, the functor that we get back should be the same as the original functor > 2. composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one

1は、 fmap id = id を満たすこと
2は、 fmap (f . g) = fmap f . fmap g を満たすこと

Applicative Functor

1つの引数を受け取るようなfunctionのマッピングは、Functorで十分機能するが、複数の引数を取る関数はどうだろうか

$ fmap (*) (Just 3)
# -> Just (* 3)

(Just (* 3)) (Just 5) のような計算方法を知らないのでこれ以上一般には、計算できない。

class (Functor f) => Applicative f where  
    pure :: a -> f a  
    (<*>) :: f (a -> b) -> f a -> f b  

要するに

# Functor
(*) <$> Just 2 <*> Just 8
# Applicative Functor
Just (*) <*> Just 2 <*> Just 8

Monad

モチベーションは、Applicative Functor とは逆で a -> m b のような function を m a に適用して m b を返すにはどうするか
そんな関数を bind と呼ぶ、また bind の定義は、

(>>=) :: (Monad m) => m a -> (a -> m b) -> m b  

Monad の 定義は以下

class Monad m where  
    return :: a -> m a  
  
    (>>=) :: m a -> (a -> m b) -> m b  
  
    (>>) :: m a -> m b -> m b  
    x >> y = x >>= \_ -> y  
  
    fail :: String -> m a  
    fail msg = error msg  

しかし return(>>=) 以外はほとんどユーザーレベルが触ることはない。

Monad は、合成の容易性が高い 例えば、

# Pair の左と右に値を追加する処理
addLeft n (left,right) = (left + n,right)  
addRight n (left,right) = (left,right + n)  


return (0,0) >>= addRight 2 >>= addLeft 2 >>= addRight 2
# -> (4,2)

またラムダ式との組み合わせが良い

Just 9 >>= \x -> return (x*10) 
# -> 90

# ネストしたラムダ式
Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))
# -> Just "3!"

# Just は Maybe なので Nothing に置き換えても動く
Just 3 >>= (\x -> Nothing >>= (\y -> Just (show x ++ y))
# -> Nothing

ラムダ式との相性がいいので、do式 という syntax が容易されている

foo :: Maybe String  
foo = do  
    x <- Just 3  
    y <- Just "!"  
    Just (show x ++ y)  

更なる学習に向けて

State Monad など いわゆる Monad の実際の例などは、Learn You a Haskell for Great Good! - For a Few Monads More を読むと良い