Monad转换器栈生成的Nothing为何不等于Nothing?
你的表达式 runIdentityT $ runMaybeT $ maybeQuit (Just 'q') 是多态类型:MonadPlus f => f (Maybe (Maybe Char))。这意味着它可以适配任何实现了 MonadPlus 的类型 f,而GHCI会根据不同的操作上下文自动选择不同的 f 实例,这就是你看到矛盾行为的核心原因:
1. 直接打印时的行为
GHCI为了输出可读性强的结果,会默认选择 Maybe 作为 f 的实例。此时,表达式的值是 Nothing :: Maybe (Maybe (Maybe Char)),所以打印出来就是 Nothing,和你看到的结果一致。
2. 使用== Nothing或isNothing时的行为
当你调用 isNothing(expr) 时,isNothing 的类型是 Maybe a -> Bool,这要求 expr 必须是 Maybe 类型。GHCI为了满足这个类型约束,会将 expr 的类型推断为 Maybe (Maybe (Maybe Char)),但此时它选择的是**return Nothing 而非 mzero**:
- 在
Maybemonad中,return Nothing的值是Just Nothing,而不是Nothing。 - 因此
isNothing (Just Nothing)返回False,这和你看到的结果一致。
3. 强制使用[]实例时的行为
当你明确将底层Monad指定为 [](列表),runMaybeT $ maybeQuit (Just 'q') 返回的是 return Nothing :: [Maybe (Maybe Char)],也就是 [Nothing]。用 head 取第一个元素得到 Nothing,isNothing 自然返回 True,符合预期。
解决方法:明确指定类型
要避免多态带来的行为不一致,你需要明确指定表达式的类型:
比如强制使用 Maybe 实例:
λ> isNothing (runIdentityT $ runMaybeT $ maybeQuit (Just 'q') :: Maybe (Maybe (Maybe Char))) -- 若表达式值为Nothing则返回True,若为Just Nothing则返回False
或者直接使用 Maybe 作为底层Monad测试:
λ> runMaybeT $ maybeQuit (Just 'q') :: Maybe (Maybe Char) Nothing λ> isNothing (runMaybeT $ maybeQuit (Just 'q') :: Maybe (Maybe Char)) True
内容的提问来源于stack exchange,提问作者Enlico

