我正在玩zipWith
并遇到以下情况:
Prelude Control.Applicative> :t zipWith id
zipWith id :: [b -> c] -> [b] -> [c]
为什么编译器期望下一个参数是一个函数列表?
我试图分析,但无法断定,为什么下一个参数必须是函数列表。
当我将id
传递给zipWith
时,签名是如何应用的?
答案 0 :(得分:14)
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
,因此它计算元素列表{{1 }}
例如:
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
是元素映射。
答案 1 :(得分:5)
谢谢Willem Van Onsem的回答。
让我们从ghc的类型推理系统的角度理解
zipWith id
。
首先,考虑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)
需要一个arument a并给出b -> c
,即一个参数函数。)
但是,只有当a -> (b -> c)
为(b - > c)时,才能使a
成为身份函数。
当a
为(b - > c)时,函数a -> b -> c
变为((b - > c) - >(b - > c))
因此,类型推理系统会将a
推断为(b -> c)
,结果输出为[a] -> [b] -> [c]
,将a
替换为b -> c
。
将
a
替换为(b - > c)。Make(a - > b - > c)看起来像
id
。通过上述替换,可以使(a - > b - > c)看起来像id
。((b - > c) - > b - > c)也可写为((b - > c) - >(b - > c)),{{1其中
id :: x -> x
是(b - > c)
x
最后我们得到输出为zipWith :: ((b -> c) -> b -> c) -> [b -> c] -> [b] -> [c]