0
0

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 1 year has passed since last update.

[Haskell] Either 型と IO 型の相互変換

Last updated at Posted at 2023-11-03

Either 型から IO 型へ直接変換する関数は標準では提供されないため、either 関数を用いて変換します。

IO 型から Either 型へ変換するには try 関数または tryJust 関数を用います。

-- 
either error                  pure :: Either String a -> IO a
either errorWithoutStackTrace pure :: Either String a -> IO a

either throwIO pure :: Exception e => Either e a -> IO a

-- 
try     :: Exception e =>                   IO a -> IO (Either e a)
tryJust :: Exception e => (e -> Maybe b) -> IO a -> IO (Either b a)

参考「either - Data.Either
参考「try - Control.Exception
参考「tryJust - Control.Exception

モナドの計算を中断する方法については別記事にしています。

参考「[Haskell] モナドの計算を中断する - Qiita

1. Either a b -> IO b

値が Left であれば例外を投げ、値が Right であれば中身の値を IO 型に入れます。

(本記事では深く触れませんが、値が Left のときに例外を投げずデフォルト値に置き換えたい場合は fromRight 関数を用いて Either a b -> b の変換をします。)

参考「fromRight - Data.Either

1.1. Either String a -> IO a

either error pure または either errorWithoutStackTrace pure とすることで Either String a -> IO a の変換を行えます。

import Control.Exception (catch, ErrorCall)
import System.IO (hPrint, stderr)

main :: IO ()
main = do

    -- 
    do

        let x = Right 23 :: Either String Int

        y :: Int <- either error pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

    do

        let x = Left "Error" :: Either String Int

        y :: Int <- either error pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

    -- 
    do

        let x = Right 23 :: Either String Int

        y :: Int <- either errorWithoutStackTrace pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

    do

        let x = Left "Error" :: Either String Int

        y :: Int <- either errorWithoutStackTrace pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

1.2. Exception e => Either e a -> IO a

either throwIO pure とすることで Exception e => Either e a -> IO a の変換を行えます。

import Control.Exception (catch, throwIO)
import System.IO (hPrint, stderr)

main :: IO ()
main = do

    do

        let x = Right 23 :: Either IOError Int

        y :: Int <- either throwIO pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: IOError)

    do

        let x = Left $ userError "Error" :: Either IOError Int

        y :: Int <- either throwIO pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: IOError)

1.3. その他

その他の Either a b 型の場合は a 型の値を String または Exception e => e 型に変換し、前述の方法を用いて IO b 型にします。

import Control.Exception (catch, ErrorCall)
import System.IO (hPrint, stderr)

f :: Int -> IO Int
f x = do
    error $ "Error: " ++ show x

main :: IO ()
main = do

    do

        let x = Right 23 :: Either Int Int

        y :: Int <- either f pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

    do

        let x = Left 42 :: Either Int Int

        y :: Int <- either f pure x :: IO Int

        print y

      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)

2. IO a -> IO (Either b a)

2.1. Exception e => IO a -> IO (Either e a)

try 関数を用いると、例外を投げられていれば Left の値を返し、そうでなければ Right の値を返します。

import Control.Exception (ErrorCall, throwIO, try)

main :: IO ()
main = do

    -- 
    do

        x :: Either ErrorCall Int <- try $ do
            pure 23 :: IO Int

        print x

    do

        x :: Either ErrorCall Int <- try $ do
            error "Error" :: IO Int

        print x

    do

        x :: Either ErrorCall Int <- try $ do
            errorWithoutStackTrace "Error" :: IO Int

        print x

    -- 
    do

        x :: Either IOError Int <- try $ do
            pure 23 :: IO Int

        print x

    do

        x :: Either IOError Int <- try $ do
            throwIO $ userError "Error" :: IO Int

        print x

2.2. Exception e => (e -> Maybe b) -> IO a -> IO (Either b a)

tryJust 関数も try 関数と同様ですが、例外を投げられていれば他の型に変換してから Left の値を返します。

例外が投げられていなければ try 関数と同様にそのまま Right の値を返します。

(tryJust 関数は他の使い方がありますが、本記事では不説明。)

import Control.Exception (ErrorCall, throwIO, tryJust)

main :: IO ()
main = do

    -- 
    do

        x :: Either String Int <- tryJust
            (\e -> pure $ show (e :: ErrorCall))
            $ do
                pure 23 :: IO Int

        print x

    do

        x :: Either String Int <- tryJust
            (\e -> pure $ show (e :: ErrorCall))
            $ do
                errorWithoutStackTrace "Error" :: IO Int

        print x

    -- 
    do

        x :: Either String Int <- tryJust
            (\e -> pure $ show (e :: IOError))
            $ do
                pure 23 :: IO Int

        print x

    do

        x :: Either String Int <- tryJust
            (\e -> pure $ show (e :: IOError))
            $ do
                throwIO $ userError "Error" :: IO Int

        print x
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?