最近やっとモナドの持ち上げ方を理解した気分になりました。StateT + IO 限定で。
State モナドを使って以下のようなコードを書いていたとき、
module Main(main) where import Control.Monad.State -- 階乗計算 fact :: State (Int, Int) Int fact = do (a, b) <- get -- (A) if a == 0 then return b else do modify $ \(a, b) -> (a-1, a*b) fact main :: IO () main = do let x = evalState fact (10, 1) print x
コメントの (A) の位置で (a, b) の値を表示したくなったときに使える技です。
module Main(main) where import Control.Monad.State -- 階乗計算 fact :: StateT (Int, Int) IO Int -- (1) fact = do (a, b) <- get liftIO $ print (a, b) -- (2) if a == 0 then return b else do modify $ \(a, b) -> (a-1, a*b) fact main :: IO () main = do x <- evalStateT fact (10, 1) -- (3) print x
変更箇所はたった 3 箇所。
- (1) fact の型を State a b から StateT a IO b に書き換える (そもそも fact に型注釈してなければ必要ない) 。
- (2) (A) の箇所に好きな IO モナドを書く。ただしその前に liftIO をつける。
- (3) fact の型変更に合わせて呼び出しを変える。
以下愚痴。「モナドのすべて」 (に限らず Haskell の文献全般) の説明は、「いきなり定義を言って、無駄にややこしい例を出して、終わり」というのが多くて悲しいです*1。1 を聞いて 10 を知るというか、ペアノ算術を聞いて 10 を知るような人ならすぐに理解するんでしょうけどねえ。