为什么这个Haskell代码编译?

时间:2012-01-20 01:02:28

标签: haskell

鉴于:

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

调用uncurry id会产生类型为(b -> c, b) -> c

的函数

我们如何得到这个结果?

如何使用id(a - > a)作为uncurry的第一个参数,这需要(a - > b - > c)函数?

2 个答案:

答案 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格式,其中ab -> 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 -> a1a2 -> (b -> c)的类型统一。这很简单,a1 ~ a2a1 ~ (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