复制:
cabal repl --build-depends=mtl-prelude,transformers
λ> import Data.Mayb
λ> import Control.Monad
λ> import Control.Monad.Trans.Identity
λ> import MTLPrelude
λ> :{
ghci| maybeQuit :: MonadPlus m => Maybe Char -> MaybeT m (Maybe Char)
ghci| maybeQuit key = do
ghci| case key of
ghci| Just 'q' -> mzero
ghci| Just '\ESC' -> mzero
ghci| _ -> return key
ghci| :}
λ> runIdentityT $ runMaybeT $ maybeQuit (Just 'c')
Just (Just 'c')
λ> runIdentityT $ runMaybeT $ maybeQuit (Just 'q')
Nothing
到目前为止,一切都很好。
但随后:
λ> (runIdentityT $ runMaybeT $ maybeQuit (Just 'q')) == Nothing
False
λ> isNothing (runIdentityT $ runMaybeT $ maybeQuit (Just 'q'))
False
什么?!
我确实看到这个表达有点多态,
λ> :t runIdentityT $ runMaybeT $ maybeQuit (Just 'q')
runIdentityT $ runMaybeT $ maybeQuit (Just 'q')
:: MonadPlus f => f (Maybe (Maybe Char))
但我完全不确定这怎么能暗示印刷的内容Nothing
可能是除之外的其他内容(==) Nothing
。
就上下文而言,我maybeQuit
在一个单子堆栈(其中有MaybeT
和其他变压器)中运行IO
,底部有一个,它按预期工作,但它的实现,因此类型,不需要的功能IO
,所以我试图在除之外的单子中对其进行测试IO
,这就是我提出这个问题的原因。
现在回想起来,我确实开始看到一些我不太理解的东西:我看到的:t runMaybeT $ maybeQuit (Just 'q')
是MonadPlus m => m (Maybe (Maybe Char))
,这正是我所期望的,但是然后将其应用于runIdentityT
它给出了类型MonadPlus f => f (Maybe (Maybe Char)
,这是同样的东西,所以我一定是误解了的含义/目的IdentityT
,并不禁想到这就是我没有得到我认为应该在原始例子中得到的东西的全部原因。
鉴于上述原因,我尝试通过应用来强制m === []
表达,结果更符合我的预期:runMaybeT $ maybeQuit (Just 'c')
head
λ> isNothing (head $ runMaybeT $ maybeQuit (Just 'q'))
True
λ> isNothing (head $ runMaybeT $ maybeQuit (Just 'c'))
False
但我还是不明白使用有什么问题runIdentityT
。
为了理解发生了什么,请考虑这个更简单的例子:
当你直接输入 时
weird
,GHCi 默认m
为IO
(因此整体类型为IO (Maybe a)
),然后为你评估 IO 操作,结果为Nothing
。当你输入weird == Nothing
或 时isNothing weird
,m
被强制为Maybe
(因此整体类型为Maybe (Maybe a)
),因此return Nothing
变为Just Nothing
,这与 不同Nothing
。您的例子有几个额外的间接层,但到最后,还是发生了同样的事情。
runIdentityT
是一个转移注意力的话题;您的表达方式是如此多态,以至于它在原来的地方根本不起作用。如果您对为什么您的表达式与 基本相同感到困惑
return Nothing
,请记住mzero
您使用的 定义如下:不是这个: