第二个参数怎么变成一个函数列表呢?
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 ~ d
和 a ~ 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+1
是 2.0
,5/4
是 1.25
,3*2
是 6.0
而 3-5
是 -2.0
.
因此 zipWith id
将采用两个元素 f
和 x
,并在这些元素上应用 id f x
,或更详细的 (id f) x
。由于 id f
是 f
,因此它将计算 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 id
。 id
的类型来自 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]
我正在玩 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 ~ d
和 a ~ 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+1
是 2.0
,5/4
是 1.25
,3*2
是 6.0
而 3-5
是 -2.0
.
因此 zipWith id
将采用两个元素 f
和 x
,并在这些元素上应用 id f x
,或更详细的 (id f) x
。由于 id f
是 f
,因此它将计算 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 id
。 id
的类型来自 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 likeid
by the above replacement.((b -> c) -> b -> c) which can also be written as ((b -> c) -> (b -> c)) which is
id :: x -> x
wherex
is (b -> c)
zipWith :: ((b -> c) -> b -> c) -> [b -> c] -> [b] -> [c]
所以最后我们得到输出 [b -> c] -> [b] -> [c]