有一个众所周知的问题我们不能使用forall类型在Cont返回类型 https://stackoverflow.com/questions/7178919/how-to-make-callcc-more-dynamic/7180154#7180154.
不过,有以下定义应该没问题:
class Monad m => MonadCont' m where
callCC' :: ((a -> forall b. m b) -> m a) -> m a
shift :: (forall r.(a -> m r) -> m r) -> m a
reset :: m a -> m a
然后找到一个有意义的例子。在这张纸 http://www.carlssonia.org/ogi/mdo-callcc.pdf作者声称我们可以实施MonadFix
在之上ContT r m
提供了m
实施的MonadFix
and MonadRef
。但我认为如果我们确实有一个MonadRef
我们实际上可以实现callCC'
上面就像下面这样:
--satisfy law: mzero >>= f === mzero
class Monad m => MonadZero m where
mzero :: m a
instance (MonadZero m, MonadRef r m) => MonadCont' m where
callCC' k = do
ref <- newRef Nothing
v <- k (\a -> writeRef ref (Just a) >> mzero)
r <- readRef ref
return $ maybe v id r
shift = ...
reset = ...
(不幸的是我不熟悉语义shift
and reset
所以我没有为他们提供实现)
这个实现对我来说似乎没问题。直观地讲,当callCC'
被召唤,我们喂养k
其自身效果总是失败的函数(尽管我们无法提供任意类型的值b
,但我们总能提供mzero
类型的m b
并且根据法律,它应该有效地停止计算所有进一步的影响),并且它捕获接收到的值作为最终结果callCC'
.
所以我的问题是:
此实现是否按理想的预期工作callCC
?我们可以实施吗shift
and reset
也有正确的语义吗?
除了上述内容之外,我还想知道:
为了确保正确的行为,我们必须假设以下属性MonadRef
。那么法律会怎样规定MonadRef
为了使上述实现按预期运行?
UPDATE
事实证明,上述简单的实现还不够好。使其满足“持续电流”
callCC $\k -> k m === callCC $ const m === m
我们必须调整实施
instance (MonadPlus m, MonadRef r m) => MonadCont' m where
callCC' k = do
ref <- newRef mzero
mplus (k $ \a -> writeRef ref (return a) >> mzero) (join (readRef ref))
换句话说,原来的MonadZero
还不够,我们必须能够结合mzero
值与正常计算而不取消整个计算。
以上并没有回答问题,只是由于最初的尝试被伪造为候选人而进行了调整。但对于更新后的版本来说,原来的问题仍然是问题。尤其,reset
and shift
仍有待实施。