为什么`hoist`约束了这个monad的类型参数?

时间:2013-08-28 12:05:07

标签: haskell monads

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

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

这样的monad的两个实例,其中on是“内部”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

指出a中的ms必须属于任意类型:ms :: Monad m => m a

为什么会这样,并且有一种方法可以使用特定参数的monad组合tma

我可以看到提升机的签名是:

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

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

2 个答案:

答案 0 :(得分: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类型检查为String,您就会得到:

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

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

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

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

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

答案 1 :(得分:2)

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

您尝试使用的函数(\ma -> comp ma ms)的类型为m a -> m String,因此它与hoist所期望的完全相反,因为它保留了容器({{1} })相同但改变了类型参数(从ma)。

我认为在这种情况下你实际上想要代替String的是一个提升monadic函数来处理变换monad的函数,所以代替hoist你需要的东西如下:

MFunctor