如何理解"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)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 -> aliftM2的论证类型统一起来,(a -> b -> c)?诀窍是将a -> a中的a替换为(a -> b)给我们(a -> b) -> (a -> b)或等效地(a -> b) -> a -> b 。(作为一个简洁的旁注,这是$的类型,这意味着$只是id受限类型!

现在我们将(a -> b) -> a -> bliftM2的整个类型结合起来:

Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

我们将a1替换为 a -> ba2替换为 ar替换为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是将函数应用程序提升到单子上liftM2 ($)给你ap是有道理的,因为这就是ap的根本含义......id只是具有更通用类型的$版本。

最新更新