我有以下代码:
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.State
type T = StateT Int IO Int
someMaybe = Just 3
f :: T
f = do
x <- get
val <- lift $ do
val <- someMaybe
-- more code in Maybe monad
-- return 4
return 3
Run Code Online (Sandbox Code Playgroud)
当我使用do内部符号在Maybemonad中工作时,它会失败.从它给出的错误看起来像这样的类型签名do不匹配.但是我不知道如何解决它.我尝试了一些lift组合,但没有一个工作,我不想再猜测了.
问题是它Maybe不是变压器堆栈的一部分.如果您的变压器只知道StateT Int并且IO,它不知道如何提升Maybe.
您可以通过将类型更改为以下内容来解决此问题T:
type T = StateT Int (MaybeT IO) Int
Run Code Online (Sandbox Code Playgroud)
(你需要导入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
Run Code Online (Sandbox Code Playgroud)
这有点尴尬,所以你可能想写一个像这样的函数liftMaybe:
liftMaybe = MaybeT . return
Run Code Online (Sandbox Code Playgroud)
如果您曾经在代码的其他部分lift提升IO a值,那么现在会因为变换器堆栈中有三个级别而中断.您将收到如下错误:
Couldn't match expected type `MaybeT IO t0'
with actual type `IO String'
Run Code Online (Sandbox Code Playgroud)
要解决此问题,您应该使用liftIO所有原始IO a值.这使用类型类IO通过任意数量的变换器层进行生命操作.
回应你的评论:如果你只有一些代码依赖Maybe,那么将do符号的结果放入变量并与之匹配会更容易:
let maybeVal = do val <- someMaybe
-- more Maybe code
return 4
case maybeVal of
Just res -> ...
Nothing -> ...
Run Code Online (Sandbox Code Playgroud)
这意味着Maybe代码将无法执行IO.您也可以自然地使用类似的功能fromMaybe而不是case.