5

我玩了一下zipWith,遇到以下情况:

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

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

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

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

4

2 回答 2

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 ~ 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将采用两个元素fand x,并应用于id f x这些,或者更详细(id f) x的 。既然id ff,就会这样计算f x

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

于 2017-08-04T14:33:15.357 回答
5

谢谢您,Willem Van Onsem 的出色回答。

让我们zipWith id从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一个参数函数。)

但是,只有当is (b -> c)a -> (b -> c)时才有可能制作恒等函数。a

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

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

替换a为 (b -> c)。

使 (a -> b -> c) 看起来像id. (a -> b -> c) 可以id通过上述替换看起来像。

((b -> c) -> b -> c) 也可以写成 ((b -> c) -> (b -> c)) 这id :: x -> x就是x(b -> c)

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

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

于 2017-08-04T15:23:54.917 回答