问题是它Maybe
不是您的变压器堆栈的一部分。如果你的变压器只知道StateT Int
和IO
,它不知道如何抬起Maybe
。
您可以通过将类型更改T
为以下内容来解决此问题:
type T = StateT Int (MaybeT IO) Int
(您需要导入Control.Monad.Trans.Maybe
。)
你还需要改变你的内在do
来工作MaybeT
而不是Maybe
。这意味着用 包装原始Maybe a
值MaybeT . return
:
f :: T
f = do
x <- get
val <- lift $ do
val <- MaybeT $ return someMaybe
-- more code in Maybe monad
return 4
return 3
这有点尴尬,所以你可能想写一个像这样的函数liftMaybe
:
liftMaybe = MaybeT . return
如果您曾经在代码的其他部分lift
提升IO a
值,现在这将中断,因为您的转换器堆栈中现在有三个级别。您将收到如下所示的错误:
Couldn't match expected type `MaybeT IO t0'
with actual type `IO String'
要解决此问题,您应该使用liftIO
所有原始IO a
值。这使用一个类型类来IO
通过任意数量的转换器层来执行操作。
回应您的评论:如果您只有一些代码取决于Maybe
,那么将do
符号的结果放入变量并与之匹配会更容易:
let maybeVal = do val <- someMaybe
-- more Maybe code
return 4
case maybeVal of
Just res -> ...
Nothing -> ...
这意味着Maybe
代码将无法进行 IO。您也可以自然地使用类似的函数fromMaybe
来代替case
.