给定:
uncurry :: (a-> b -> c) -> (a,b) -> c
id :: a -> a
调用 uncurry id
的结果是一个类型为:(b -> c, b) -> c
的函数。
我们如何得到这个结果呢?
你如何使用 id (a -> a) 作为 uncurry 的第一个参数,而 uncurry 需要一个 (a -> b -> c) 函数?
给定:
uncurry :: (a-> b -> c) -> (a,b) -> c
id :: a -> a
调用 uncurry id
的结果是一个类型为:(b -> c, b) -> c
的函数。
我们如何得到这个结果呢?
你如何使用 id (a -> a) 作为 uncurry 的第一个参数,而 uncurry 需要一个 (a -> b -> c) 函数?
如果我们从“使类型匹配”的角度来看待问题,那么理解起来会更容易:找出我们需要做什么来id
的类型以适应uncurry
所需的形状。 因为我们有:
id :: a -> a
我们还有:
id :: (b -> c) -> (b -> c)
当我们在原始的id
类型中将a
替换为b -> c
时,就像计算id 42
的类型时可以用Int
替换一样,我们可以看到这一点。然后,我们可以去掉右边的括号,因为(->)
是右结合的:
id :: (b -> c) -> b -> c
展示了id
的类型符合形式a -> b -> c
,其中a
是b -> c
。换句话说,我们可以通过特化它已经拥有的通用类型来重塑id
的类型以适应所需的形式。
另一种理解这个概念的方法是将uncurry ($)
的类型也视为(b -> c,b) -> c
。比较id
和($)
的定义:
id :: a -> a
id a = a
($) :: (a -> b) -> a -> b
($) f x = f x
我们可以使后面的定义更加point-free:
($) f = f
此时,($)
只是 id
更特定类型的一种特殊情况,这一点变得清晰明了。
uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id :: a -> a
uncurry id
调用时需要将类型 a1 -> a1
与 a2 -> (b -> c)
统一。这很简单,a1 ~ a2
并且 a1 ~ (b -> c)
。这只是机械操作,不需要创造性思维。因此,所讨论的 id
实际上具有 a -> a where a ~ (b -> c)
类型,因此 uncurry id
具有类型 (b -> c,b) -> c
,通过将 a ~ (b -> c)
替换为 (a,b) -> c
得到。也就是说,它期望一个 b -> c
函数和一个 b
值的一对,并且必须产生一个 c
值。c
值的 唯一方式 就是使用 b
值作为参数 调用 b -> c
函数。当然,这就是 ($)
所做的事情。因此,uncurry id == uncurry ($)
,尽管 id
明显不是 ($)
。
uncurry id
而不是uncurry($)
是邪恶的 :) - ehird(\
id` 3)代替
$ 3或将
id定义为
infixr 0 `id`并使用它来替代
$,例如
(^2) `id` 1 + 2 * 3`。 - Vitus