id :: a -> a
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap = liftM2 id
您是否可以帮助解释在ap
应用于liftM2
时如何推断id
的类型?此外,问同等问题是否有效,但更具体地说,(a -> b -> c) -> m a
在这种情况下如何减少到m (a -> b)
?
答案 0 :(得分:6)
让我们试着找出liftM2 id
的类型。首先,我们更改id
中的类型参数,以便我们可以更轻松地解决这个问题。顺便说一下,id
的类型为a -> a
,而不是a -> b
:
id :: x -> x
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
接下来,我们在liftM2
中添加其他括号,并记住a -> b -> c
实际上是a -> (b -> c)
:
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
现在我们转移x -> y
,以便它们与liftM2
中的其他类型对齐:
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
确定。这告诉我们a ~ (b -> c)
中的liftM2 id
或:
id :: (b -> c) -> (b -> c)
liftM2 :: (Monad m) => ((b -> c) -> (b -> c))
-> m (b -> c) -> (m b -> m c)
现在我们可以使用这些专业版本:
liftM2 id :: Monad m => m (b -> c) -> (m b -> m c)
我们删除了多余的括号,最后得到了正确的类型:
liftM2 id :: Monad m => m (b -> c) -> m b -> m c
答案 1 :(得分:4)
id
的类型为a -> a
。第一个问题是我们如何统一 a -> a
liftM2
的论证类型(a -> b -> c)
?诀窍是用a
替换a -> a
中的(a -> b)
给我们(a -> b) -> (a -> b)
或等同于(a -> b) -> a -> b
。 (作为一个简洁的侧面说明,这是$
的类型,这意味着$
只是id
的限制类型!)
现在我们将(a -> b) -> a -> b
与liftM2
的整个类型结合起来:
Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
我们将a1
替换为a -> b
,将a2
替换为a
,将r
替换为b
,然后向我们发送:
Monad m => ((a -> b) -> a -> b) -> m (a -> b) -> m a -> m b
最后,一旦我们将liftM2
应用于id
,结果将具有相同的类型减去第一个参数:
liftM2 id :: Monad m => m (a -> b) -> m a -> m b
我们是:ap
的类型。
对此的良好直觉是基于我之前关于$
的观察。 $
是正常的函数应用程序运算符; ap
是函数应用程序解除了monad 。 liftM2 ($)
为您提供ap
是有道理的,因为ap
从根本上意味着...... id
只是$
的一个版本更通用的类型。