也许monad里面有一堆变形金刚

Adr*_*ian 6 monads haskell

我有以下代码:

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组合,但没有一个工作,我不想再猜测了.

Tik*_*vis 8

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