为什么 `mfix` 在 `MaybeT` 中不完全
Why is `mfix` not total in `MaybeT`
如果函数的计算结果为 Nothing
,MonadFix
的 transformers implementation for MaybeT
将失败。为什么 Nothing
没有传播到 mfix
?
mfix' :: MonadFix m => (a -> MaybeT m a) -> MaybeT m a
mfix' f = MaybeT $ mfix $ \case
Nothing -> return Nothing
Just x -> runMaybeT $ f x
一定有一个我看不到的充分理由,因为 ListT
根本没有实现 MonadFix
,而 Maybe
如上所述实现了它 in the same way。
我认为问题只是 error
消息具有误导性。让我们只关注 MonadFix Maybe
。 mfix
的参数可以是以下四种之一。
输入可以是严格的:f _|_ = _|_
或“f
需要评估其输入以决定是否returnNothing
或 Just
"
\x -> if x then Nothing else Just True
\x -> x `seq` Nothing
\x -> x `seq` Just x
可以是const Nothing
.
可以是Just . f
其中f
不严格
Just . (1:)
可以是Just . f
,其中f
是严格的。
Just
如果函数是严格的,那么整个事情就会在无限循环中爆炸(就像 fix
),并且看不到错误,因为我们不知道我们是否会有一个Nothing
或 Just
。如果它是 const Nothing
,该函数实际上从未尝试计算 error
,并且什么也没有发生。如果它是 Just . f
并且 f
不严格,那么它只是 Just $ fix f
(根据法律:mfix $ return . f = return $ fix f
)。而且,如果 f
是严格的,我们会得到 Just _|_
(同样,根据法律)。请注意,我们从未看到 error
被触发。
类似的推理适用于 MonadFix (MaybeT m)
。我觉得这次还是举个例子比较好:
runMaybeT $ mfix $ \x -> MaybeT $
(x `seq` Nothing) :
Nothing :
(Just (1:x)) :
(Just x) :
(x `seq` [])
我上面列出的四个案例中的每一个都在该列表中。结果的第一个元素是一个无限循环。第二个是Nothing
。第三个是repeat 1
,第四个是Just
无限循环。尝试访问超出此范围的 "elements" 会触发另一个无限循环,这次是由 []
的 MonadFix
而不是 MaybeT
引起的。同样,我认为不可能触发 error
,因为该函数必须在已经确定结果为 Nothing
.
之后强制执行参数
bomb
的定义在引用的库定义中确实非常混乱,尽管函数本身已正确实现。根据单调性,任何满足 f undefined = Nothing
的函数 f
必须等于 const Nothing
。因此,fixed-point 计算将简单地产生包含在转换器堆栈中的 Nothing
的正确答案。
有关详细信息,请参阅 this work 的第 4.9 节,尽管为清楚起见,原始定义省略了构造函数,并且使用名称 ErrT
代替了 MaybeT
。 (那时候还没有MTL!)那里的定义是这样的:
mfixErrM :: (α → ErrT m α) → ErrT m α
mfixErrM f = mfixM (f · unErr)
where unErr (Ok a) = a
附录 B.7 中还有一个证明表明只要基础 mfixM
是有效的 value-recursion,这就是 ErrT m
的有效 value-recursion 运算符] m
.
的运算符
如果函数的计算结果为 Nothing
,MonadFix
的 transformers implementation for MaybeT
将失败。为什么 Nothing
没有传播到 mfix
?
mfix' :: MonadFix m => (a -> MaybeT m a) -> MaybeT m a
mfix' f = MaybeT $ mfix $ \case
Nothing -> return Nothing
Just x -> runMaybeT $ f x
一定有一个我看不到的充分理由,因为 ListT
根本没有实现 MonadFix
,而 Maybe
如上所述实现了它 in the same way。
我认为问题只是 error
消息具有误导性。让我们只关注 MonadFix Maybe
。 mfix
的参数可以是以下四种之一。
输入可以是严格的:
f _|_ = _|_
或“f
需要评估其输入以决定是否returnNothing
或Just
"\x -> if x then Nothing else Just True \x -> x `seq` Nothing \x -> x `seq` Just x
可以是
const Nothing
.可以是
Just . f
其中f
不严格Just . (1:)
可以是
Just . f
,其中f
是严格的。Just
如果函数是严格的,那么整个事情就会在无限循环中爆炸(就像 fix
),并且看不到错误,因为我们不知道我们是否会有一个Nothing
或 Just
。如果它是 const Nothing
,该函数实际上从未尝试计算 error
,并且什么也没有发生。如果它是 Just . f
并且 f
不严格,那么它只是 Just $ fix f
(根据法律:mfix $ return . f = return $ fix f
)。而且,如果 f
是严格的,我们会得到 Just _|_
(同样,根据法律)。请注意,我们从未看到 error
被触发。
类似的推理适用于 MonadFix (MaybeT m)
。我觉得这次还是举个例子比较好:
runMaybeT $ mfix $ \x -> MaybeT $
(x `seq` Nothing) :
Nothing :
(Just (1:x)) :
(Just x) :
(x `seq` [])
我上面列出的四个案例中的每一个都在该列表中。结果的第一个元素是一个无限循环。第二个是Nothing
。第三个是repeat 1
,第四个是Just
无限循环。尝试访问超出此范围的 "elements" 会触发另一个无限循环,这次是由 []
的 MonadFix
而不是 MaybeT
引起的。同样,我认为不可能触发 error
,因为该函数必须在已经确定结果为 Nothing
.
bomb
的定义在引用的库定义中确实非常混乱,尽管函数本身已正确实现。根据单调性,任何满足 f undefined = Nothing
的函数 f
必须等于 const Nothing
。因此,fixed-point 计算将简单地产生包含在转换器堆栈中的 Nothing
的正确答案。
有关详细信息,请参阅 this work 的第 4.9 节,尽管为清楚起见,原始定义省略了构造函数,并且使用名称 ErrT
代替了 MaybeT
。 (那时候还没有MTL!)那里的定义是这样的:
mfixErrM :: (α → ErrT m α) → ErrT m α
mfixErrM f = mfixM (f · unErr)
where unErr (Ok a) = a
附录 B.7 中还有一个证明表明只要基础 mfixM
是有效的 value-recursion,这就是 ErrT m
的有效 value-recursion 运算符] m
.