第二个参数怎么变成一个函数列表呢?

How does the second parameter become a list of functions?

我正在玩 zipWith 并遇到以下问题:

Prelude Control.Applicative> :t zipWith id
zipWith id :: [b -> c] -> [b] -> [c]

为什么编译器期望下一个参数是函数列表?

我试图分析,但无法得出结论,为什么下一个参数必须是函数列表。

当我将 id 传递给 zipWith 时,签名是如何应用的?

zipWith的类型是:

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

id的类型是:

id :: d -> d

所以如果我们现在想要导出 zipWith id 的类型,我们将 id :: d -> d 的类型推入 zipWith 的第一个参数的类型:

  d -> d
~ a -> (b -> c)

所以这意味着:a ~ da ~ b -> c。所以这意味着 zipWith id 的类型现在是:

   zipWith id :: [a] -> [b] -> [c]
-> zipWith id :: [b -> c] -> [b] -> [c]

这是如何工作的:第一个列表必须包含函数列表 f :: b -> c,第二个列表必须包含元素列表 x :: b,因此它计算元素列表 f x :: c.

例如:

Prelude> zipWith id [(+1),(5/),(3*),(3-)] [1,4,2,5]
[2.0,1.25,6.0,-2.0]

因为 1+12.05/41.253*26.03-5-2.0.

因此 zipWith id 将采用两个元素 fx,并在这些元素上应用 id f x,或更详细的 (id f) x。由于 id ff,因此它将计算 f x.

因此我们可以得出结论,zipWith 是一个 elementwise 映射。

谢谢 Willem Van Onsem 的精彩回答。

Let's understand zipWith id from the eyes of the type inference system of ghc.

首先,考虑zipWith

的类型
Prelude> :info zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    -- Defined in ‘GHC.List’

zipWith 的第一个参数是一个接受两个参数的函数。

(a -> b -> c)也可以改写为a -> (b -> c)

现在考虑 zipWith idid 的类型来自 a -> a

我们已经把 id 放在了一个必须有两个参数的函数的地方。

因此,类型推断会使 (a -> b -> c) 看起来像 a -> (b -> c)(注意 a -> (b -> c) 接受一个参数 a 并给出 b -> c,即单个参数函数。)

但是,只有当 a 是 (b -> c) 时,才能使 a -> (b -> c) 成为恒等函数。

a 为 (b -> c) 时,函数 a -> b -> c 变为 ((b -> c) -> (b -> c))

因此,类型推断系统会将 a 推断为 (b -> c),结果输出将是 [a] -> [b] -> [c]a 替换为 b -> c

Replace a with (b -> c).

Make (a -> b -> c) look like id. (a -> b -> c) can be made to look like id by the above replacement.

((b -> c) -> b -> c) which can also be written as ((b -> c) -> (b -> c)) which is id :: x -> x where x is (b -> c)

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

所以最后我们得到输出 [b -> c] -> [b] -> [c]