鉴于:
uncurry :: (a-> b -> c) -> (a,b) -> c
id :: a -> a
Run Code Online (Sandbox Code Playgroud)
uncurry id在类型函数中调用结果:(b -> c, b) -> c
我们如何得到这个结果?
如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?
ehi*_*ird 25
如果我们尝试从使类型变得有效的角度来看待它,就会更容易理解:弄清楚我们需要做什么id才能使它适合所需的形状uncurry.既然我们有:
id :: a -> a
Run Code Online (Sandbox Code Playgroud)
我们还有:
id :: (b -> c) -> (b -> c)
Run Code Online (Sandbox Code Playgroud)
这可以通过替换可以看出b -> c对于a原型的id,就像你可以替代Int搞清楚的类型时代替id 42.然后我们可以将括号放在右侧,因为它(->)是右关联的:
id :: (b -> c) -> b -> c
Run Code Online (Sandbox Code Playgroud)
示出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
Run Code Online (Sandbox Code Playgroud)
我们可以使后一个定义更加无点:
($) f = f
Run Code Online (Sandbox Code Playgroud)
在这一点上,($)只是id对更具体类型的特化的事实变得清晰.
如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?
实际上,uncurry需要(a -> (b -> c))功能.您看得出来差别吗?:)
省略括号是邪恶的(有时候).这使得新手无法破译Haskell.当然,在你收集了一些语言经验之后,你觉得你根本不再需要它们了.
在这里,一旦我们明确地写出所有省略的括号,一切都变得清晰了:
uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id :: a -> a
Run Code Online (Sandbox Code Playgroud)
现在,写作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 -> c函数的b值一个论点.当然,这是做什么的($).所以uncurry id == uncurry ($),虽然id肯定不是 ($).