从 monad 到 IO monad - 简单且惯用的方式?

Either monad to IO monad - simple and idiomatic way?

此代码编译:

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"

哪里没有:

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

如何将 Either monad 转换为 IO one?我想要一种简单且惯用的方式,不需要额外的库。

BS.readFile "my_config.yaml" >>= Y.decodeEither' 没有多大意义,因为 decodeEither' :: FromJSON a => ByteString -> Either ParseException a 本身不是 return 一个 IO a,并且 BS.readFile 不是一个 Either ParseException b,所以它既没有 IO 单子上下文,也没有 Either a 单子上下文。

你可以做的是对结果执行仿函数映射,所以 Y.decodeEither' <$> BS.readFile "my_config.yaml",但是它的类型是 FromJSON a => IO (Either ParseException a),所以你不能使用 Left 的模式匹配和Right

但是您可以将其实现为:

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

或者您可以像 那样在 case 部分执行映射:

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

此处无需使用doliftIO

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

应该做。

但是使用 ExceptT transformer 可能会更好,那么您需要做的就是

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

现在你可以像这样了;

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

或如@Joseph Sible-Reinstate Monica 所提醒的那样

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

如果 decodeEither returns 一个 Left 值,它会被记录并 processConfig :: Config -> () 被跳过。

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

在上面的错误尝试中,它几乎看起来像你想要的 -XLambdaCase:

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

但我发现 either 更具可读性:

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