IO (Maybe a)

IO (Maybe a)

1a679952cdf455ecd6a15cbde7ae80d5?s=128

Tomohiko Himura

July 03, 2016
Tweet

Transcript

  1. IO (Maybe a) 2016-06-05 LTۦಈ։ൃ26

  2. ͻΉΒ ͱ΋ͻ͜ ϞφϞφ

  3. IO (Maybe a)

  4. IO Maybe a == (IO Maybe) a 型としては無効(カインドが合わない)

  5. IO (Maybe a)

  6. IO String 実行するたびに文字列が返ってくる。 返ってくる文字列の内容は実行するたびに変わるかもしれない。 Maybe String 評価すると文字列もしくは空っ ぽかもしれない値が返ってくる。評価するたびに結果は変わらな い。 IO

    (Maybe String) 実行するたびに文字 列もしくは空っぽの値が返ってくる。値は実行するたびに変わる かもしれない。
  7. ghci> :kind IO IO :: * -> * ghci> :kind

    Maybe Maybe :: * -> * ghci> :kind String String :: * ghci> :kind Maybe String Maybe String :: * IOやMaybeは * をうけとって *を返す。 * であるStringを渡すと * になる。 Maybe Stringは * なので IOに渡せる。 Maybe は * -> * なので IOに渡せない。
  8. ࣮ࡍͷ࿩

  9. 環境変数を取得する ghci> :type System.Environment.lookupEnv System.Environment.lookupEnv :: String -> IO (Maybe

    String) 変数名があれば値がとれる。 値は実行するタイミングによっては変わる 値はないこともある。
  10. $ stack ghci ghci> :type System.Environment.lookupEnv "Hoge" System.Environment.lookupEnv "Hoge" ::

    IO (Maybe String) ghci> System.Environment.lookupEnv "Hoge" Nothing Hogeは未定義なので値はとれない
  11. Hogeの値がとれる。 結果は実行するたびに違う可能性がある。 $ Hoge="abc" stack ghci ghci> System.Environment.lookupEnv "Hoge" Just

    "abc" $ Hoge="" stack ghci ghci> System.Environment.lookupEnv "Hoge" Just ""
  12. 値をつかってみる

  13. 愚直にdoを使ってみる import System.Environment import Data.Maybe sample = do -- IOͷͨΊͷdo

    maybeHoge <- lookup "Hoge" -- maybeHoge ͸ IO͕ͱΕͯ Maybe Stringɹͱ͍͏ܕ ʹͳ͍ͬͯΔ return $ do -- Maybe ͷͨΊͷdo IOͷdoͷதʹ͔͚ΔΑ͏ʹ return Λ͚ͭΔ ɹɹ hoge <- maybeHoge -- hoge͸Maybe͕͸ͣΕͯ String ɹɹ return . putStrLn $ hoge -- hogeͷ಺༰Λग़ྗɻίϯύΠϧ͕௨ΔΑ͏ʹ returnΛ ͚ͭͯ͋͛Δ sampleの型は IO (Maybe (IO ())) Hogeが定義されていたら、標準出力に内容を出力。未定義 だったときのことが考慮されていないため、3重になって いる doが二重になってあたりもつらい。
  14. さっき作ったsampleを使う $ stack ghci ghci> sample >>= fromMaybe (putStrLn "not

    found Hoge") not found Hoge 未定義の時の処理を追加して使う。 Hogeが未定義なのでfromMaybeで追加し た処理が動いている $ Hoge=aaa stack ghci ghci> sample >>= fromMaybe (putStrLn "not found Hoge") aaa こっちはHogeがあるのでHogeの内容 が表示されている
  15. 実行するのが大変。。。 値がなかった時の処理を組み込んでおく

  16. 値がない場合はnot foundに 型は IO () sample2 = do maybeHoge <-

    lookupEnv "Hoge" let hoge = fromMaybe "not found" maybeHoge -- ͜ͷ࣌఺ͰMaybeΛ֎͓ͯ͘͠ɻ NothingͳΒ "not found"ʹͳΔ putStrLn hoge $ stack ghci ghci> sample2 not found $ Hoge="aaa" stack ghci ghci> sample2 aaa 使うのも簡単
  17. しかし 処理が追加できる柔軟性を
 失ってしまった

  18. IO と Maybeの組み合わせではなく IOとMaybeをまとめて扱えたらいいのに

  19. *0 .BZCF 4USJOH IO (Maybe String) X String 9 4USJOH

  20. それ MaybeT でできるよ

  21. * -> * を受け取れる ghci> :kind MaybeT MaybeT :: (*

    -> *) -> * -> * ghci> :kind MaybeT IO MaybeT IO :: * -> * ghci> :kind IO IO :: * -> * ghci> :kind Maybe Maybe :: * -> *
  22. *0 .BZCF 4USJOH IO (Maybe String) MaybeT IO String .BZCF5*0

    4USJOH
  23. MaybeT IO Stringはメンバに IO (Maybe String)を持っているだけの構造 簡単に行ったり来たりできる .BZCF5*0 4USJOH *0

    .BZCF 4USJOH runMaybeT MaybeT
  24. sample3の型は MaybeT IO String import System.Environment import Data.Maybe import Control.Monad.Trans.Maybe

    sample3 = do hoge <- MaybeT $ lookupEnv "Hoge" -- IO (Maybe String)Λ MaybeT IO Stringʹม׵ͨ͠ɻശ͸ҰͭͳͷͰ <- Ͱ஋Λͻͬͺͯ͘Δͱ Stringʹͳ͍ͬͯΔ lift $ putStrLn hoge -- IO () ͳͷͰ MaybeT IO () ʹม׵
  25. sample3を使ってみる $ stack ghci ghci> runMaybeT sample3 -- IO (Maybe

    String)ʹͳΔͷͰ࣮ߦͰ͖ͯɺ Maybe Stringʹ ͳΔ Nothing ghci> runMaybeT sample3 >>= maybe (putStrLn "not found") (const return ()) -- ޙ͔Βॲཧͷ௥Ճ΋Ͱ͖Δ "notFound" $ Hoge=aaa stack ghci ghci> runMaybeT sample3 aaa ghci> runMaybeT sample3 >>= maybe (putStrLn "not found") (const return ()) aaa ghciはIO aな型は実行してaにしてくれる機能があるっぽい。
 MaybeTをつかうと1重のモナドとして扱えて便利。 元に戻すこともできる
  26. sample3と同じ動きをするように書いてみる sample1_5 = do maybeHoge <- lookupEnv "Hoge" case maybeHoge

    of Just hoge -> putStrLn hoge >> return (Just ()) Nothing -> return Nothing 一度Maybeを自分で解いて、再度同じようになるように値を構 築している。最初の例はMaybeを解かずにdoをつかったため、 IO (Maybe (IO ())) となってしまった。
  27. 4USJOH .BZCF 4USJOH .BZCF 4USJOH *0 .BZCF5*0 4USJOH *0 4USJOH

    return return return return runMaybeT MaybeT lift
  28. まとめ

  29. まとめ • MaybeTをつかうと • 2重のモナドが1重のように扱える • 2重のモナドといったりきたりできる • MaybeTにする方法が用意されてるので •

    MaybeTに統一すれば操作が簡単