Stateモナドが(なんとなく)わかった
Stateモナドがわからない。ということで「入門Haskell」にのっていたBase64エンコードのソースコードを写経。
そしたらなんとなくわかってきた。
procChar :: Int -> State B64EncState String procChar c = get >>= transit where transit First = do put $ Second (c .&. 3) return [table !! shiftR c 2] transit (Second n) = do put $ Third (c .&. 15)) return [table !! (shiftL n 4 .|. shiftR c 4)] transit (Third n) = do put First return [table !! (shiftL n 2 .|. shiftR c 6), table !! (c .&. 63)]
写経した当初は、「まずgetで状態を取得して」というのがピンとこず。
式のどこにも状態がないじゃん。getで取得する状態ってどこにあるのよ?と。
が、Stateが
newtype State s a = State {runState :: (s -> (a, s))}
ということを思いだすとようやくピンときた。
Stateというのはデータではなく、関数だ。
上記のprocCharは「State B64EncState String」型を返すということは、データを返しているわけではなく、関数を返しているのだな。
で、
b64Enc s = do strs <- mapM (procChar . ord ) s suffix <- gets calcSuffix return (concat strs ++ suffix)
として作った「b64Enc s」も「State B64EncState String」型を返す。
そして、
base64Encode s = evalState (b64Enc s) First
とevalStateをかますことで実際の計算がなされると。このときにやっとgetで取得される状態が渡される。
「関数型言語において、関数はファーストクラスオブジェクトである」という言葉を知ってはいたが、きっちりと実感、理解できていなかったからだろう。「State s a」というのが関数にみえてなかった。関数であると理解したら、なんとなくStateモナドがわかった気がする。
よし、わかった気がするでとめるのはもったいないので、Stateモナドを使って何か書こう。さて、何書こう。