LoginSignup
8
5

More than 5 years have passed since last update.

MaybeとStateを合成

Last updated at Posted at 2015-04-24

Haskellの実験メモです。

MaybeとStateをモナド変換子で合成してみました。積む順番を変えた2パターンを試しました。

モナド変換子の知識を前提としています。

この記事には続編のようなものがあります。

発端

プログラミング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 <- getx: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,"")

リンク

当該書籍に関係する記事です。

StateTとリストによるパーサーを解説した記事です。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5