我有一些组成两个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
如何影响我正在尝试做的事情,如果它有任何影响。
答案 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} })相同但改变了类型参数(从m
到a
)。
我认为在这种情况下你实际上想要代替String
的是一个提升monadic函数来处理变换monad的函数,所以代替hoist
你需要的东西如下:
MFunctor