如何理解 "ap = liftM2 id" 的类型推断?
How to understand type inference of "ap = liftM2 id"?
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
你能帮忙解释一下当liftM2
应用于id
时,ap
的类型是如何推断出来的吗?此外,提出等效问题是否有效,但更具体地说,在这种情况下 (a -> b -> c) -> m a
是如何减少到 m (a -> b)
的?
让我们试着找出 liftM2 id
的类型应该是什么。首先,我们更改 id
中的类型参数,以便我们可以更轻松地解决此问题。
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 -> x
使其与 liftM2
中的其他类型对齐:
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
好的。这告诉我们 a ~ (b -> c)
in 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
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
只是 $
的一个版本,具有更通用的类型.
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
你能帮忙解释一下当liftM2
应用于id
时,ap
的类型是如何推断出来的吗?此外,提出等效问题是否有效,但更具体地说,在这种情况下 (a -> b -> c) -> m a
是如何减少到 m (a -> b)
的?
让我们试着找出 liftM2 id
的类型应该是什么。首先,我们更改 id
中的类型参数,以便我们可以更轻松地解决此问题。
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 -> x
使其与 liftM2
中的其他类型对齐:
id :: x -> x
liftM2 :: (Monad m) => (a -> (b -> c)) -> m a -> (m b -> m c)
好的。这告诉我们 a ~ (b -> c)
in 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
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
只是 $
的一个版本,具有更通用的类型.