Haskell:在IO中处理失败的常见模式::IO(Either String Int)

6

尝试理解处理IO内可能出现的失败模式。如果只有像下面这样的一个case,那么这可能是可以接受的,但如果嵌套了一堆嵌套的IO (Either String Int),是否有一种常见的模式来处理这些类型呢?例如,如果functionDoSomething中的b再次是一个(Either a b),并且在成功获取值并再次执行某些操作时会出现另一个类似的case。是否有高阶函数可以使用?我对单子变换器还不熟悉,不确定它们是否可以用于处理此特定的单子堆栈。如果它们可以在这里使用,是否有一种方法可以在不使用它们的情况下完成。

import Control.Monad

functionCreate :: Int -> IO (Either String Int)
functionDoSomething :: Int -> IO b

functionUse :: IO ()
functionUse = do
   created <- functionCreate 10
   case created of
      (Right v)        -> void $ functionDoSomething v
      _                -> return ()
1个回答

9

我知道你新来到Haskell,而monad transformers并不是你想要理解的第一个概念。然而,在这种情况下,它确实是使用的模式。

一般来说,monad使你可以像“穿梭于functors之间”一样操作。如果你只有Either,你可以用do符号将Right值从函数中拉出来,并跳过Left的情况。

但在这种情况下,你有一个嵌套的monads:“IO中含有Either”。因此,当你尝试使用do符号时,你需要进入IO的上下文,也就是说,你通过箭头<-提取的值仍然是Either的值,如OP所示。

Monad transformers使你能够将monads堆栈(如本例中的IO内部的Either)视为Monad实例,以便你可以在堆栈上使用do符号等操作。

虽然你期望Either monad transformer被称为EitherT,但由于各种原因,它被称为ExceptT。你可以像这样稍微简化OP代码:

import Control.Monad (void)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except

functionUse :: IO (Either String ())
functionUse = runExceptT $ do
  created <- ExceptT $ functionCreate 10
  liftIO $ void $ functionDoSomething created

在这里,created是一个Int值,可以传递给functionDoSomething函数。

IO (Either String ()) 也就是 ExceptT String IO (),对吗? - chepner
@chepner它们是两种不同的类型,但是它们是同构的。ExceptT向一侧前进,而runExceptT向另一侧前进。 - Mark Seemann
@chepner ExceptT是一个newtype,所以从某种意义上说你是对的。 - Mark Seemann
啊,是的,我明白了。 - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接