这可能有助于深入了解MaybeT底层的情况...
在IO单子中,我们有计算,例如:
putStrLn "Hello, world!" :: IO ()
getContents :: IO String
等等。所有这些计算都具有类型IO a
,实际上,类型变量a
是不受限制的-它可以是任何类型。使用return
,我们可以为任何类型a
创建一个IO计算IO a
。
您可以将MaybeT IO
视为返回Maybe值的那些IO计算。
例如,getContents
是一个IO
计算,但不是MaybeT IO
计算。但是,有一种明显的方法将getContents
转换为MaybeT IO
计算-只需在其输出中包装Just
构造函数即可:
lift getContents === fmap Just getContents
lift
是将一个 IO
计算提升为一个 MaybeT IO
计算的方法。将其应用于 return_IO
(IO
单子的 return
函数),我们有:
return_(MaybeT IO) === lift return_IO === fmap Just return_IO
也就是说,在 MaybeT IO
Monad 中,return 3
相当于在 IO
Monad 中使用 return (Just 3)
。
runMaybeT 和 MaybeT 是互相反转的。
现在看一下 MaybeT
的定义
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
这也描述了在 MaybeT m
中的计算与返回 Maybe 值的 m
中的计算相同的想法。以下函数:
MaybeT :: m (Maybe a) -> MaybeT m a
runMaybeT :: MaybeT m a -> m (Maybe a)
它们是彼此的逆元素,实际上除了添加或删除 newtype 包装器之外并不做任何事情。在 IO
monad 的情况下:
MaybeT :: IO (Maybe a) -> MaybeT IO a
runMaybeT :: MaybeT IO a -> IO (Maybe a)
考虑你问题中的例子,我们可以看到在
MaybeT (Either String)
中的计算与任何类型
a
的
Either String (Maybe a)
的值本质上是相同的。这导致了以下可能性:
Left
某个字符串
Right (Just ...)
Right Nothing
这些都存在于
Either String (Maybe a)
中,如果我们将构造函数
MaybeT
应用于它们中的每一个,我们就得到了对应的值
MaybeT (Either String) a
。
特别地,要找到一个
y
使得
runMaybeT y == Right Nothing
只需使用:
y = MaybeT (Right Nothing)
再次强调,runMaybeT
和MaybeT
是彼此的反函数,因此您可以将MaybeT
应用于两侧以获得相同的结果。