问题是它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.