简体   繁体   English

为什么这个Haskell代码编译?

[英]Why does this Haskell code compile?

Given: 鉴于:

uncurry :: (a-> b -> c) -> (a,b) -> c    
id :: a -> a

Invoking uncurry id results in a function of type: (b -> c, b) -> c 调用uncurry id产生类型为的函数: (b -> c, b) -> c

How do we get this result? 我们如何得到这个结果?

How can you use id (a -> a) as the first parameter to uncurry, which requires a (a -> b -> c) function? 如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?

It's easier to understand if we try and look at it from the point of making the types work out: figuring out what we need to do to id 's type to get it to fit the shape required by uncurry . 如果我们从制作类型的角度来看它,就会更容易理解弄清楚我们需要对id的类型做些什么才能使它适合uncurry所需的形状。 Since we have: 既然我们有:

id :: a -> a

we also have: 我们还有:

id :: (b -> c) -> (b -> c)

This can be seen by substituting b -> c for a in the original type of id , just as you might substitute Int instead when figuring out the type of id 42 . 这可以通过替换可以看出b -> ca在原始类型的id ,就像你可以替代Int搞清楚的类型时,而不是id 42 We can then drop the parentheses on the right-hand side, since (->) is right-associative: 然后我们可以将括号放在右侧,因为(->)是右关联的:

id :: (b -> c) -> b -> c

showing that id 's type fits the form a -> b -> c , where a is b -> c . 表明id的类型适合形式a -> b -> c ,其中ab -> c In other words, we can reshape id 's type to fit the required form simply by specialising the general type it already has. 换句话说,我们可以通过专门化它已经拥有的一般类型来重塑id的类型以适应所需的形式。

Another way to understand this is to see that uncurry ($) also has the type (b -> c, b) -> c . 理解这一点的另一种方法是看到uncurry ($)也有类型(b -> c, b) -> c Comparing the definitions of id and ($) : 比较id($)的定义:

id :: a -> a
id a = a

($) :: (a -> b) -> a -> b
($) f x = f x

we can make the latter definition more point-free: 我们可以使后一个定义更加无点:

($) f = f

at which point the fact that ($) is simply a specialisation of id to a more specific type becomes clear. 在这一点上, ($)只是对特定类型的id ,这一事实变得清晰。

How can you use id (a -> a) as the first parameter to uncurry, which requires a (a -> b -> c) function? 如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?

Actually, uncurry requires (a -> (b -> c)) function. 实际上, uncurry需要(a -> (b -> c))函数。 Can you spot the difference? 您看得出来差别吗? :) :)

Omitting parentheses is evil (well, sometimes). 省略括号是邪恶的(有时候)。 It makes it impossible for a novice to decipher Haskell. 这使得新手无法破译Haskell。 Of course after you've gathered some experience with the language, you feel like you don't need them at all, anymore. 当然,在你收集了一些语言经验之后,你觉得你根本不需要它们了。

Here, it all becomes clear once we write out all the omitted parentheses back explicitly: 在这里,一旦我们明确地写出所有省略的括号,一切都变得清晰了:

uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id      ::  a ->    a

Now, writing uncurry id calls for a type unification of a1 -> a1 with a2 -> (b -> c) . 现在,编写uncurry id调用a1 -> a1a2 -> (b -> c)的类型统一。 This is straightforward, a1 ~ a2 and a1 ~ (b -> c) . 这很简单, a1 ~ a2a1 ~ (b -> c) Just mechanical stuff, no creative thinking involved here. 只是机械的东西,这里没有创造性思维 So id in question actually has type a -> a where a ~ (b -> c) , and so uncurry id has type (b -> c,b) -> c , by simple substitution of a ~ (b -> c) into (a,b) -> c . 所以问题中的id实际上有类型a -> a where a ~ (b -> c) ,因此uncurry id具有类型(b -> c,b) -> c ,通过简单替换a ~ (b -> c)进入(a,b) -> c That is, it expects a pair of a b -> c function and a b value, and must produce a c value. 也就是说,它需要一对b -> c函数和一个b值,并且必须产生一个c值。

Since the types are most general (ie nothing is known about them, and so there's no specific functions to call that might do the trick in some special way), the only way to produce a c value here is to call the b -> c function with the b value as an argument. 由于类型是最通用的(即没有任何关于它们的知识,因此没有特定的函数来调用可能以某种特殊方式执行该技巧),这里生成c值的唯一方法调用 b -> c使用b值作为参数。 Naturally, that's what ($) does. 当然,这就是($)作用。 So uncurry id == uncurry ($) , although id is most certainly not ($) . 所以uncurry id == uncurry ($) ,虽然id肯定不是 ($)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM