Haskellの(>>)ってもしかしてすごい便利じゃない?
(>>) :: Monad m => m a -> m b -> m b
- 今までの印象
- 型からして明らかに常に右を取る
- do式ののりづけ関数
- 今回気づいたこと
- 値の世界について言及されていないから、左の値を取ることがある
-
m >> n = n
といった法則は要求されてない
-
- 値の世界について言及されていないから、左の値を取ることがある
事の発端
事の発端はMonadThrow
の(>>)
の要請するこの法則を見た時に気づきを得た。
throwM x >> y = throwM x
Maybe
やEither e
もMonadThrow
インスタンスなので、上記の法則を満たすっぽい!
例えばMaybe
はこう
Nothing >> Just 10 = Nothing
Either
はこう。
Left "error" >> Right 10 = Left "error"
これってすごい便利じゃない?
活用する
hoge :: IO a -- 例外を発する可能性がある
foo :: IO b -- 例外を発する可能性がある
bar :: IO c -- 例外を発する可能性がある
このような関数があった時、こんなmain
は嫌だよね!! X(
main :: IO ()
main = do
x <- try hoge
y <- try foo
z <- try bar
-- いずれかで例外が発生していればプログラムを終了したい
case x of
Left e -> print (e :: SomeException)
Right _ ->
case y of
Left e' -> print (e' :: SomeException)
Right _ ->
case z of
Left e'' -> print (e'' :: SomeException)
Right _ -> putStrLn "The program is succeed"
こうする。
main :: IO ()
main = do
x <- try hoge
y <- try foo
z <- try bar
-- いずれかで例外が発生していればプログラムを終了したい
let a = x >> y >> z
case a of
Left e -> print (e :: SomeException)
Right _ -> putStrLn "The program is succeed"
これ
x >> y >> z
は、単なるこんな感じのintegrate
関数の略記バージョンだ! :D
integrate :: Either e a1 -> Either e a2 -> Either e a3 -> Either e a3
integrate e1 e2 e3 = do
x <- e1
y <- e2
z <- e3
return z
実際に試す用のコードを載っけておくね。
import Control.Monad.Catch (try, SomeException)
hoge :: IO Int -- 例外を発する可能性がある
hoge = return 10
foo :: IO Char -- 例外を発する可能性がある
foo = fail "foo is failed"
bar :: IO Bool -- 例外を発する可能性がある
bar = return True
main :: IO ()
main = do
x <- try hoge
y <- try foo
z <- try bar
-- いずれかで例外が発生していればプログラムを終了したい
let a = x >> y >> z
case a of
Left e -> print (e :: SomeException)
Right _ -> putStrLn "The program is succeed"
応用
こんなこともできる :D
firstLeftOrLastRight :: [Either e a] -> Either e a
firstLeftOrLastRight = foldr1 (>>)
main :: IO ()
main = do
let rights = [Right 1, Right 2, Right 3, Right 4] :: [Either String Int]
let someLeft = [Right 1, Right 2, Left "oops!", Right 4]
print $ foldr1 (>>) rights
print $ foldr1 (>>) someLeft
便利!!