重现:
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
的力量,所以我试图在一个 monad 中测试它,而不是IO
,这就是我提出这个问题的方式。
事后看来,我确实开始看到一些我不太明白的东西:我看到
:t runMaybeT $ maybeQuit (Just 'q')
是MonadPlus m => m (Maybe (Maybe Char))
,这是我所期望的,但是然后应用runIdentityT
给出类型MonadPlus f => f (Maybe (Maybe Char)
,这是相同的事情,所以我一定误解了IdentityT
的含义/目的,并且忍不住认为这就是整个原因是我没有得到我认为我应该在原始示例中得到的东西。
要了解发生了什么,请考虑这个更简单的示例:
ghci> import Data.Maybe
ghci> weird = return Nothing
ghci> weird
Nothing
ghci> weird == Nothing
False
ghci> isNothing weird
False
ghci> :t weird
weird :: Monad m => m (Maybe a)
ghci>
当您刚输入
weird
时,GHCi 默认 m
为 IO
(因此整体类型为 IO (Maybe a)
),然后为您评估 IO 操作,结果为 Nothing
。当输入weird == Nothing
或isNothing weird
时,m
被强制为Maybe
(所以整体类型为Maybe (Maybe a)
),所以return Nothing
变成了Just Nothing
,这与Nothing
不同。
您的示例有几个额外的间接层,但最终,同样的事情正在发生。