不可能为MonadWriter派生MonadWriter实例吗?

时间:2011-03-02 11:07:04

标签: haskell

我尝试为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动作必须计算两次。可能有人拿这个暗示将这两个电话以某种方式合并成一个吗?然后我们将获得所需的实现。

1 个答案:

答案 0 :(得分:9)

我不认为这是可能的。作为参考,这里是ContT

的含义
ContT r m a = (a -> m r) -> m r

这是我listen的起点:

listen m = ContT $ \c -> 
    runCont m (\x -> c (x,w))

问题是,我们从哪里获得ww将来自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 DoRecMonadFix 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是问题所在。如果你能想出一个结合listencallCC会产生悖论的例子,我不会感到惊讶。