generated at
モナド
概要
haskell
class Applicative m => Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b

計算エフェクト m がついている a 型の値を扱うときに便利。

(>>=) :: m a -> (a -> m b) -> m b の左辺に値 m a を入れると、右辺の関数 a -> m b 内では a 型の(純粋な)値として扱うことができるようになる。
haskell
v :: Monad m => a -> m a v x = compute x >>= return where compute :: a -> m a compute x = undefined

しかし >>= を多用すると fp-ts chain のように、いわゆるコールバック地獄になって見た目がよろしくない。
haskell
vv :: (Monad m, Num b) => b -> b -> b -> m b vv x y z = compute x >>= \x' -> compute y >>= \y' -> compute z >>= \z' -> return $ x' + y' + z'

そこで do 記法を使う。
haskell
vv :: (Monad m, Num b) => b -> b -> b -> m b vv x y z = do x' <- compute x y' <- compute y z' <- compute z return $ x' + y' + z'

計算エフェクト m がついている a 型の値を扱うときに、 do 記法で手続き的に(あるいは具体的な構造をモナドのインスタンスによって隠蔽して)処理を書けるので便利。
ApplicativeDo? 知らない子ですね…。

モナド則
モナドとは圏論から来ている言葉で、圏論は代数の知り合いである。
モナドにもequation rulesがある。
haskell
1. return x >>= f === f x 2. m >>= return === m 3. (m >>= f) >>= g === m >>= (\x -> f x >>= g)
これはモナドが持っておくべき性質だが、これを知らないと死んだり知らないでHaskellを書くとOSがクラッシュするなどの事態にはならない。

計算エフェクト とはなんですか?
多くの誤解を生むが広く知られている言葉でいえば 副作用 です。
ある文脈では m Maybe だったり、またあるときは IO になる。
なるほど、 lookup :: [(String, a)] -> String -> Maybe a などのありふれた関数が返す値 Maybe a というのを 副作用として扱いたいときにモナドがあると、あたかも Maybe a ではなく a が返ってきたように書けるわけだな。
haskell
env :: [(String, Int)] env = [("x", 3), ("y", 10)] evaled :: Maybe Int evaled = do -- Monad Maybe インスタンスをガンガン活用する x <- lookup env "x" y <- lookup env "y" z <- lookup env "z" -- Nothing return (x + y + z) -- だが何事もなかったかのように記述できる

やれモナドだ副作用だ純粋だと言われるときによく槍玉に挙げられる IO だが、これもただたんに IO :: * -> * という装飾の付いた値が関数から返ってくるので、それを手続き的に書きたいがためにモナドを利用しているに過ぎない。

IO モナド
IO は単なる型 * → * に過ぎないと書いたが、実はそうでもない。というのも IO の表すエフェクトはファイル等の入出力 IORef に見られるメモリの参照など、処理系レベルでなんとかしないといけない操作である。 IO モナドのエンドポイントは 関数 main :: IO () であり、 main まで IO をつなげていく必要がある。
haskell
foo :: () foo = let _ = putStrLn "Hello" -- この式のIOの文脈を無視 in () -- しかも無を返す bar :: IO () bar = do _ <- putStrLn "World" -- 冗長ですが return () -- IO の文脈を継いでいる biz :: IO () biz = do let _ = putStrLn "!" -- この式のIOの文脈を無視して putStrLn "*" -- こちらを返す main :: IO () main = do _ <- return foo -- nothing to do _ <- bar -- prints `World` _ <- biz -- prints only `*` return ()

例えば上のコードでは foo は内部でIOを発生させようとしているが () を返しており、 IO の文脈は無視されている。 biz IO () を返すが最初の putStrLn はただの let で束縛しており、 IO の文脈が断絶している。 bar ではmonadic bindで putStrLn "World" を束縛しているため、次の return () IO の文脈がつながっている。このように文脈をつなげて書いていくプログラミングスタイルをmonadic programmingと呼ぶ。特にHaskellではこう書かないとIOエフェクトを発生させることができない。 unsafePerformIO ? 知らない子ですね…。

忙しい人向け
モナドは単なる自己関手の圏におけるモノイド対象だよ。何か問題でも?