Haskellの実験メモです。
MaybeとStateをモナド変換子で合成してみました。積む順番を変えた2パターンを試しました。
モナド変換子の知識を前提としています。
- Haskell モナド変換子 超入門 2015.1.4
この記事には続編のようなものがあります。
- Parsecをモナド変換子で模倣 2015.4.28
発端
『プログラミングHaskell』の『第8章 関数型パーサー』で説明されているParserは、MaybeとStateの両方の特徴を併せ持っています。モナド変換子で合成して同じことができないかと思いました。
import Control.Monad
import Control.Monad.State
import Control.Monad.Maybe
stateOnMaybe :: StateT Int Maybe ()
stateOnMaybe = do
a <- get
guard $ a == 1
modify (+ 10)
maybeOnState :: MaybeT (State Int) ()
maybeOnState = do
a <- lift get
guard $ a == 1
lift $ modify (+ 10)
main = do
print $ runStateT stateOnMaybe 1
print $ runStateT stateOnMaybe 2
print $ runState (runMaybeT maybeOnState) 1
print $ runState (runMaybeT maybeOnState) 2
Just ((),11)
Nothing
(Just (),11)
(Nothing,2)
Maybeの性質は失敗による脱出に使っています。
モナドスタックの積み方によって最終的に出て来る型が異なります。
-
stateOnMaybe
: 全体がMaybeに包まれて出て来る -
maybeOnState
: Stateの値のみMaybeに包まれて出て来る
全体がMaybeに包まれていることや、Stateアクションを持ち上げずにそのまま使えることから、stateOnMaybe
の方が便利だと思いました。
【追記】失敗時の状態を取得する必要があればmaybeOnState
を使います。
ここまで試してから、同じ方法でパーサーの動作まで確認した記事があることに気付きました。
この書き換えを見て、item = do x:xs <- get
でx:xs
へのパターンマッチが失敗してもエラーにならないのを不思議に思いました。
パターンマッチの失敗
確認するとdo
ではエラーになりませんが、>>=
で書き替えるとエラーになります。
main = do
print $ do (x:xs) <- Just ""; Just (x,xs) -- OK
print $ Just "" >>= \(x:xs) -> Just (x, xs) -- NG
Nothing
Main.hs:5:34-56: Non-exhaustive patterns in lambda
この件に関する質問と回答を探しました。
要約すると<-
から>>=
への書き替えでは失敗を受け止めるパターンが追加されるとのことです。
先ほどの例に当てはめると次のようになります。
main = do
print $ do (x:xs) <- Just ""; Just (x,xs)
print $ let ok (x:xs) = Just (x, xs)
ok _ = fail "..."
in Just "" >>= ok
Nothing
Nothing
追加テスト
モナド変換子で合成しても、パターンマッチが失敗すれば脱出として処理されるのを確認します。
import Control.Monad
import Control.Monad.State
import Control.Monad.Maybe
maybeOnly :: String -> Maybe (Char, String)
maybeOnly s = do
x:xs <- Just s
return (x, xs)
stateOnMaybe :: String -> Maybe (Char, String)
stateOnMaybe = runStateT $ do
x:xs <- get
put xs
return x
maybeOnState :: String -> (Maybe Char, String)
maybeOnState = runState $ runMaybeT $ do
x:xs <- lift get
lift $ put xs
return x
main = do
print $ maybeOnly "abc"
print $ maybeOnly ""
print $ stateOnMaybe "abc"
print $ stateOnMaybe ""
print $ maybeOnState "abc"
print $ maybeOnState ""
Just ('a',"bc")
Nothing
Just ('a',"bc")
Nothing
(Just 'a',"bc")
(Nothing,"")
リンク
当該書籍に関係する記事です。
- @ruicc: Programming Haskell Chapter8 - SlideShare 2011.11.12
- Haskellのモナドまでの12ステップ - GPソフト Wiki 2014.1.2
StateTとリストによるパーサーを解説した記事です。