3

我有一些组成两个单子的函数:

comp :: Monad m => m a -> m b -> m b

还有两个这样的单子实例,其中 on 是“内部” an Mfunctor

ms :: Monad m => m String
ms = undefined

tma :: (Monad m, MFunctor t) => t m a
tma = undefined

现在,如果我尝试ms使用tma

tmas = hoist (\ma -> comp ma ms) tma

我收到此错误:

 Could not deduce (a ~ [Char])
    from the context (Monad m, MFunctor t)
      bound by the inferred type of
               comp :: (Monad m, MFunctor t) => t m b
      at Coroutine.hs:607:1-40
      `a' is a rigid type variable bound by
          a type expected by the context: m a -> m a at Coroutine.hs:607:8
    Expected type: m a
      Actual type: m String

其中指出ainms必须是任意类型:ms :: Monad m => m a.

为什么会这样,有没有办法tma用特定参数的单子组成。

我可以看到葫芦的签名是:

hoist :: (Monad m, MFunctor t) => (forall a. m a -> n a) -> t m b -> t n b

但无法想象如何forall影响我正在尝试做的事情,如果它有任何影响的话。

4

2 回答 2

4

将参数的顺序切换为comp这样:

tmas = hoist (\ma -> comp ms ma) tma

-- or more simply:
tmas = hoist (comp ms) tma

原因是类型comp为:

comp :: (Monad m) => m a -> m b -> m b

如果你设置,ms作为第二个参数,b类型检查为 aString并且你得到:

(`comp` ms) :: (Monad m) => m a -> m String

...但是如果您设置ms为第一个参数,则a类型检查为String

(ms `comp`) :: (Monad m) => m b -> m b

后一种类型是正确的类型,hoist因为它b是普遍量化的(即“forall”ed)。

要回答您关于正确性的问题,答案是通用量化保证参数 tohoist仅修改 monad 层而不是 monad 返回值。但是,如果您也打算修改返回值,那么hoist这不是您想要的。

于 2013-08-28T14:28:02.307 回答
2

的类型hoist表示它需要一个函数(forall a. m a -> n a),即一个改变“容器”类型但保持类型参数相同的函数。这forall意味着您提供的函数不能专门用于任何特定的a,但必须适用于任何类型参数。

您尝试使用的函数 ( \ma -> comp ma ms) 具有类型m a -> m String,因此它与预期的几乎相反,hoist因为它使容器 ( m) 保持不变,但更改了类型参数(从aString)。

我认为在这种情况下,您实际上正在寻找的hoist是一个提升单子函数以处理转换后的单子的函数,因此MFunctor您需要的是:

import Control.Monad.Trans.Class

tmas :: (Monad m, Monad (t m), MonadTrans t) => t m String
tmas = transLift (\ma -> comp ma ms) tma

transLift :: (Monad m, Monad (t m), MonadTrans t) => (m a -> m b) -> t m a -> t m b
transLift f tma = tma >>= lift . f . return
于 2013-08-28T12:37:57.957 回答