zip的解释($)

时间:2013-11-21 19:51:01

标签: haskell currying

我一直在努力理解这段代码,但我无法清楚地说明这一点:

ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a -> b) -> a -> b
ghci > let c = zipWith ($)
ghci > :t c
c :: [b -> c] -> [b] -> [c]

[b -> c]如何在上述类型签名中产生?

4 个答案:

答案 0 :(得分:6)

为了zipWith ($)进行类型检查,我们必须统一类型为zipWith的{​​{1}}第一个参数的类型。我会把它们一起写出来并用独特的名字来表达它们。

($)

因此,zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] ($) :: ((x -> y) -> x -> y) 类型检查当且仅当我们可以假设zipWitha ~ (x -> y)b ~ x时。没有什么可以阻止这种统一成功,所以我们可以将这些名称替换回c ~ y的类型。

zipWith

然后继续申请,因为现在一切都很好了

zipWith :: ((x -> y) -> x -> y) -> [x -> y] -> [x] -> [y]
($)     :: ((x -> y) -> x -> y)

这相当于具有您所见类型的类型变量名称的特定选择。

答案 1 :(得分:2)

这只是上下文替换,没有魔法。看:

ghci > :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci > :t ($)
($) :: (a' -> b') -> a' -> b'

现在考虑zipWith ($)。它的类型为(a -> b -> c) -> [a] -> [b] -> [c],其中第一个参数是固定的,因此我们应该将(a -> b -> c)(第一个arg的类型)与(a' -> b') -> a' -> b'(类型$)进行模式匹配。因此,我们有a = (a' -> b')b = a'c = b'。替换回到zipWith[a' -> b'] -> [a'] -> [b'](第一个参数是固定的,所以他从类型中消失了),这正是你用不同命名的类型变量得到的。

此外,人们可能会考虑zipWith语义:拉链(第一个参数),然后将两个列表压缩在一起。如果您的拉链是函数应用程序($是函数应用程序,是的!)那么当压缩两个列表时,您只需使用第二个列表的相应元素调用第一个列表的元素。功能类型反映了这一点。

答案 2 :(得分:0)

类型签名中指定的实际字母是任意的,可以是任何字母。您可以轻松地将($)的类型写为

(x -> y) -> x -> y

它需要两个参数,一个函数接受一个参数,一个值传递给函数。

zipWith的第一个参数是一个带有两个参数(a -> b -> c)的函数。根据{{​​1}}的定义,您选择($)a(x -> y)b,然后xc,所以你得到y的类型为

zipWith ($)

答案 3 :(得分:0)

让我们重写一下我们的签名:

($) :: (a -> b) -> a -> b
($) :: a' -> b' -> c'
  where  -- pseudo-Haskell
    a' = a -> b
    b' = a
    c' = b


zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

找到,那

zipWith ($) :: [a'] -> [b'] -> [c']

zipWith ($) :: [a -> b] -> [a] -> [b]