10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IO (Maybe String)を触ってみる

Last updated at Posted at 2016-07-03

LT駆動の発表内容の下書きです。
下書きといっても十分にかいているつもりである。(読みやすくとはいっていない)

IO (Maybe String)はモナドが入れ子になっている。IOなんかついてるととても扱いづらい。実際に体験してみたりしてみる。

IO String実行するたびに文字列が返ってくる。返ってくる文字列の内容は実行するたびに変わるかもしれない。

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

IO (Maybe String)は、実行するたびに文字列もしくは空っぽの値が返ってくる。値は実行するたびに変わるかもしれない。

IO Maybe Stringではない。
(IO Maybe) Stringと解釈されるため。これは型として不正。
IOは一つの型しか受け取らないが2つの型を受け取ろうとしているからである。

カインドを確認してみる。

ghci> :kind IO
IO :: * -> *
ghci> :kind Maybe
Maybe :: * -> *
ghci> :kind String
String :: *
ghci> :kind Maybe String
Maybe String :: *

Maybe String*なのでIOに渡せるわけです。
というわけで、Maybe (IO String)という型もつくれる。

実際の話をしよう。
環境変数を取得するlookupEnvをつかってみます。

ghci> :type System.Environment.lookupEnv
System.Environment.lookupEnv :: String -> IO (Maybe String)

環境変数Hogeを取得してみる

$ stack ghci
ghci> :type System.Environment.lookupEnv "Hoge"
System.Environment.lookupEnv "Hoge" :: IO (Maybe String)
ghci> System.Environment.lookupEnv "Hoge"
Nothing

lookupEnvに引数を与えているので、型はIO (Maybe String) になっています。
結果がNothingなのはHogeが未定義のためです。

$ Hoge="abc" stack ghci
ghci> System.Environment.lookupEnv "Hoge"
Just "abc"
$ Hoge="" stack ghci
ghci> System.Environment.lookupEnv "Hoge"
Just ""

さて、実際に、IO (Maybe String)な値をつかっていくところをみていきます。

環境変数Hogeを愚直に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をつけてあげる

細かい事を気にせずかいてみました。doが二重構造になっています。

sample関数の結果はつかってみます。

$ stack ghci
ghci> sample >>= fromMaybe (putStrLn "not found Hoge")
not found Hoge
$ Hoge=aaa stack ghci
ghci> sample >>= fromMaybe (putStrLn "not found Hoge")
aaa

sampleだけでは動作しません。
Hogeがなかったときの処理を追加して、Maybeを外してやることで動作します。

sampleの型はIO (Maybe (IO ()))になっています。IO (Maybe ()になってて欲しいところがこうなってるために

sampleをつくるときにHogeがなかったときの処理を追加しておくことでこの問題を回避してみます。

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

sample2の型はIO ()になっています。
Hogeが未定義だった時の処理は埋め込まれてしまい、後で制御ができなくなってしました。

最初の例ではIOMaybeの2つのコンテナを活用していて、相互に利用していると絡まってしまいました。
ふたつめの例複雑さを抑えるためにMaybeを排除していましました。

3つめの方法では IOMaybeをくっつけて、 MaybeT IOを作ってみます。

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 () に変換
$ 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

sample3を使うにはrunMaybeTを使う必要があります。
これは MaybeT IO aIO (Maybe a)に戻すことができます。
IOにすることができたので実行できます。
sample3の特徴は、値がなかったときの処理を後から追加できることです。

この方法の良い所は、MaybeT IO aに変換するのは難しくないことです。

MaybeTを使わずに、sample3と同じような動きになるように、つくるとこんな感じになりました。

sample1_5 = do
  maybeHoge <- lookupEnv "Hoge"
  case maybeHoge of
    Just hoge -> putStrLn hoge >> return (Just ())
    Nothing -> return Nothing

Maybeの中身を一回紐解いて、IOの処理をして、再度手動でMaybe化してあげないといけなくなってます。
この作業はボイラープレートになるようなのでMaybeTを使うほうがシンプルに記述できるようです。

MaybeTへの変換は、紐解く必要がなく、何かの関数をくっつけるだけです。
よくありそうな変換を表にしてみます。

xの型 変換方法
String return x MaybeT IO String
Maybe String MaybeT . return $ x MaybeT IO String
IO String lift x MaybeT IO String
String -> String fmap x MaybeT IO String -> MaybeT IO String
String -> IO () lift . x String -> MaybeT IO ()
String -> Maybe a MaybeT . return . x String -> MaybeT IO a

Kobito.ZEMiWt.png

リンク

IO (Maybe a)というタイトルでモナド変換子について学んだことを話した - #LT駆動 - そんなこと覚えてない

10
11
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
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?