试图了解用于处理内部可能出现的故障的模式IO
。如果它只是case
像下面这样的 s,它可能是可以接受的,但是如果对一堆嵌套的IO (Either String Int)
s进行嵌套,是否有处理此类类型的通用模式。例如,如果b
infunctionDoSomething
再次是 a(Either a b)
并且在成功时获取值并再次用它做某事将是另一个这样的case
。是否有我可以使用的高阶函数?我对 monad 转换器还不满意,不确定它们是否可以用来处理这个特定的 monad 堆栈。如果它们可以在这里使用,有没有办法在不使用它们的情况下做到这一点。
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 ()
Run Code Online (Sandbox Code Playgroud)
我知道您是 Haskell 的新手,并且 monad 转换器不是您想要解决的第一个概念。然而,在这种情况下,它是要使用的模式。
可以这么说,Monads 通常使您能够“织入和织出函子”。如果您只有Either
,您可以使用Either
带do
符号的Right
值将值从值中提取出来,同时使Left
情况短路。
但是,在这种情况下,您有一堆 monad:Either
在IO
. 因此,当您尝试使用do
符号时,您处于IO
上下文中,这意味着,正如 OP 所示,您使用<-
箭头从函数中“拉出”的Either
值仍然是值。
Monad 转换器使您能够将 monad 堆栈(例如,在这种情况下,Either
内部的值IO
)视为Monad
实例,以便您可以,例如,do
在堆栈上使用符号。
虽然您希望Either
monad 转换器被称为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
Run Code Online (Sandbox Code Playgroud)
这里,created
是一个Int
值,然后可以将其传递给functionDoSomething
。