简体   繁体   中英

Either monad to IO monad - simple and idiomatic way?

This code compiles:

read :: IO Config
read = do
  c1 <- BS.readFile "my_config.yaml"
  case Y.decodeEither' c1 of
    Right x -> pure x
    Left e -> error "error 123"

where this doesn't:

read :: IO Config
read = do
  liftIO $ case (BS.readFile "my_config.yaml") >>= Y.decodeEither' of
    Right x -> pure x
    Left e -> error "error 123"

===>

  Expected type: IO Config
    Actual type: Either a1 Config

How can I transform Either monad to IO one? I'd like a simple and idiomatic way, with no additional libraries.

The line BS.readFile "my_config.yaml" >>= Y.decodeEither' does not make much sense, since decodeEither':: FromJSON a => ByteString -> Either ParseException a itself does not return an IO a , and BS.readFile is not an Either ParseException b , so it has neither an IO monadic context, or an Either a monadic context.

What you can do is perform a functor mapping on the result, so Y.decodeEither' <$> BS.readFile "my_config.yaml" , but then this has as type FromJSON a => IO (Either ParseException a) , so you can not use pattern matching with Left and Right on that.

You can however implement this as:

read :: IO Config
read = do
    result <-  BS.readFile "my_config.yaml"
    case  of
        Right x -> pure x
        Left e -> error e

or you can, like @JosephSible says perform the mapping in the case part:

read :: IO Config
read = do
    result <- BS.readFile "my_config.yaml"
    case  of
        Right x -> pure x
        Left e -> error e

No need to use do or liftIO here.

read :: IO Config
read = Y.decodeEither' <$> BS.readFile "my_config.yaml" >>= \c -> case c of
                                                                  Right x -> pure x
                                                                  Left  e -> error "error 123"

should do it.

However it could be nicer to use the ExceptT transformer then all you need to do would be

read :: ExceptT ParseException IO Config
read =  ExceptT $ Y.decodeEither <$> BS.readFile "my_config.yaml"

Now you can do like;

configure :: ExceptT ParseException IO ()
configure =  read >>= pure . processConfig

or as reminded by @Joseph Sible-Reinstate Monica like

configure :: ExceptT ParseException IO ()
configure = processConfig <$> read

and if decodeEither returns a Left value it gets logged and processConfig:: Config -> () gets skipped.

read :: IO Config
read = do
  liftIO $ case (BS.readFile "my_config.yaml") >>= Y.decodeEither' of
    Right x -> pure x
    Left e -> error "error 123"

In the above incorrect attempt it almost looks like you want -XLambdaCase:

    fmap Y.decodeEither' (BS.readFile "my_config.yaml) >>= \case
        Right x -> pure x
        Left e -> error "error 123"

But I find either more readable:

    either (error "error 123") pure . Y.decodeEither' =<< BS.readFile "my_config.yaml"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM