我尝试为Continuation Monad Transformer的MonadWriter创建一个派生实例。 这就是我尝试的方式:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
import Control.Monad.Cont
import Control.Monad.Writer
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
tell= lift . tell
listen m= ContT $ \ c -> do
(a,w) <- listen $ runContT m (c)
return (a,w)
pass m = undefined
这给了我以下错误:
Occurs check: cannot construct the infinite type: r = (r, w1)
When generalising the type(s) for `listen'
In the instance declaration for `MonadWriter w (ContT r m)'
接下来试试这个:
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
tell= lift . tell
listen m= ContT $ \ c -> do
(a,w) <- runContT m (listen . c)
return (a,w)
pass m = undefined
其次是:
Occurs check: cannot construct the infinite type: a = (a, w)
When generalising the type(s) for `listen'
In the instance declaration for `MonadWriter w (ContT r m)'
有谁知道如何实现listen并传递到这里?是否有理由在mtl中没有这样的实例声明? 请帮我理解这个!
此致 玛丽安
PS: 我发现Blog Entry at blog.sigfpe.com位于某个地方 讨论结束时Edward Kmett说:
“(...)我记得,当你开始在ContT中混音时,'pass'和'local'会导致当前MTL出现问题,并且可能会被分解为不同的类。”
对于MonadWriter来说,也许同样适合听。因此,最简单的解决方案是,如果您不需要倾听并传递特殊情况,请将它们保留为未定义:
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
tell= lift . tell
listen = undefined
pass = undefined
PS:(2011-03-11)在这个主题中进一步潜水我提出了这个解决方案: (当将ContT上的类型r指定为()时,我们可以尝试:)
instance (MonadWriter w m) => MonadWriter w (ContT () m) where
listen m = do
a <- m
(_,w) <- lift $ listen $ runContT m (return . (const ()))
return (a,w)
这个编译!跑了!但是,唉,monadic动作必须计算两次。可能有人拿这个暗示将这两个电话以某种方式合并成一个吗?然后我们将获得所需的实现。
答案 0 :(得分:9)
我不认为这是可能的。作为参考,这里是ContT
:
ContT r m a = (a -> m r) -> m r
这是我listen
的起点:
listen m = ContT $ \c ->
runCont m (\x -> c (x,w))
问题是,我们从哪里获得w
? w
将来自runCont m
执行之前的计算它调用我们的函数\x -> c (x,w)
及其返回值x
。也就是说,我们需要传递给c
的信息来自runCont
,因此我们需要执行以下操作:
listen m = ContT $ \c -> do
rec (r,w) <- listen . runContT m $ \x -> c (x,w)
return r
(上下文中需要LANGUAGE DoRec
和MonadFix m
)
虽然那种类型很严重,但这是不正确的。 w
现在是整个计算所写的值,而不仅仅是调用我们的继续\x -> c (x,w)
之前的部分。
你看到你需要做什么吗?我知道我的回答基本上是“我认为这是不可能的,因为我无法想办法”(Conal Elliott称之为“缺乏想象力的证据”),但我认为这次缺乏想象力是正确的。我们需要的信息在我们有机会窥视之前就被摧毁了。
我相信使用 Codensity monad转换器可以实现这个实例:
newtype CodensityT m a = CodensityT { runCodensityT :: forall r. (a -> m r) -> m r }
在执行此操作的情况下,与Cont
相同的性能改进,但不支持callCC
。这是因为您可以在计算过程中runCodensityT
使用您想要的任何r
。
listen m = CodensityT $ \c -> listen (runCodensityT m return) >>= c
可能callCC
是问题所在。如果你能想出一个结合listen
和callCC
会产生悖论的例子,我不会感到惊讶。