鉴于:
uncurry :: (a-> b -> c) -> (a,b) -> c
id :: a -> a
调用uncurry id
会产生类型为(b -> c, b) -> c
我们如何得到这个结果?
如何使用id(a - > a)作为uncurry的第一个参数,这需要(a - > b - > c)函数?
答案 0 :(得分:25)
如果我们尝试从使类型解决方案的角度来看待它,就会更容易理解:弄清楚我们需要对id
的类型做些什么来获取它以适应uncurry
所需的形状。既然我们有:
id :: a -> a
我们也有:
id :: (b -> c) -> (b -> c)
可以通过在b -> c
的原始类型中用a
代替id
来看到这一点,就像在Int
中找出id 42
的类型一样。 1}}。然后我们可以将括号放在右侧,因为(->)
是右关联的:
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
我们可以使后一个定义更加无点:
($) f = f
此时,($)
只是id
对更具体类型的特化,这一事实变得清晰。
答案 1 :(得分:8)
如何使用id(a - > a)作为uncurry的第一个参数,这需要(a - > b - > c)函数?
实际上,uncurry
需要(a -> (b -> c))
功能。您看得出来差别吗? :)
省略括号是邪恶的(好吧,有时候)。这使得新手无法破译Haskell。当然,在你收集了一些语言经验之后,你觉得你根本不再需要它们了。
在这里,一旦我们明确地写出所有省略的括号,一切都变得清晰了:
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
值。
由于类型是最通用的(即没有任何关于它们的知识,因此没有特定的函数可以通过某种特殊的方式调用它),只能生成一个{ {1}}此处的值是调用 c
函数,其中b -> c
值作为参数。当然,这就是b
所做的。所以($)
虽然uncurry id == uncurry ($)
肯定是不是 id
。