MaybeT 计算中的类型

Types in MaybeT computation

在 IO 计算中,我得到了一个 case mbValue of … 的阶梯,我发现我应该使用 Maybe monad 来简化代码。因为它在 IO 计算中,我需要获得 IO 值,所以我使用了 MaybeT monad 转换器,这样我就可以 lift IO 计算到 Maybe.

现在,我一直在考虑在 Maybe 计算中的 values <- mbValue 之后将值“剥离”其 Maybeness,但事实证明这太简单了这里是启发式。

如下所示,当使用 Maybe a 值作为 a 时(此处将其传递给 read),它无法键入检查:

import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT)

lol :: IO (Maybe Int)
lol = return (Just 3)

lal :: IO (Maybe String)
lal = return (Just "8")

foo :: IO (Maybe Bool)
foo = do
  b <- runMaybeT $ do
    x <- lift lol
    y <- lift lal
    return (x < (read y)) 
  return b       ^-- Couldn't match type ‘Maybe String’ with ‘String’

main = foo >>= print

如果我在 return (x < (read y)) 中输入一个打字洞,我发现它需要一个 Bool,这是有道理的,而且当前的绑定包括

||   y :: Data.Maybe.Maybe GHC.Base.String
||     (bound at /private/tmp/test.hs:14:5)
||   x :: Data.Maybe.Maybe GHC.Types.Int
||     (bound at /private/tmp/test.hs:13:5)

yMaybe String。这当然解释了错误,但我很困惑。我的理解哪里错了,我该如何解决这个错误?

简而言之:用 MaybeT 构造函数替换 lift

注意

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

lift :: (MonadTrans t, Monad m) => m a -> t m a

您在

中使用lift
x <- lift lol

类型为

lift :: IO (Maybe Int) -> MaybeT IO (Maybe Int)

这就是 x 将再次成为 Maybe Int 的原因。 lift 添加一个新的 MaybeT 层,该层独立于您已有的 Maybe 事件。

但是

MaybeT :: m (Maybe a) -> MaybeT m a

而不是

x <- MaybeT lol

将用于类型

MaybeT :: IO (Maybe a) -> MaybeT IO a

并做正确的事。

专用于MaybeTlift :: Monad m => m a -> MaybeT m a。因为lol :: IO (Maybe Int)mIOaMaybe Int,所以lift lol :: MaybeT IO (Maybe Int).

IO (Maybe a) 只是包含在 MaybeT IO a 新类型包装器中的值,因此无需解除它;而是使用 MaybeT 构造函数,例如 MaybeT lol

但这不是人们倾向于使用 monad 转换器的方式。相反,只需使用 MaybeT 值并根据需要提升:

import Control.Monad
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT, MaybeT)

lol :: MaybeT IO Int
lol = return 3

lal :: MaybeT IO String
lal = return "8"

foo :: IO (Maybe Bool)
foo = 
  runMaybeT $ do
    x <- lol
    y <- lal

    _ <- lift getLine -- lift (IO String) to MaybeT IO String
    _ <- return 100   -- lift any pure value    
    _ <- mzero        -- use the MonadPlus instance to get a lifted Nothing.

    return (x < (read y)) 

main = foo >>= print