How to interpret compiler messages 'Couldn't match type: [b0] with: [a2] -> t :'?

huangapple go评论65阅读模式
英文:

How to interpret compiler messages 'Couldn't match type: [b0] with: [a2] -> t :'?

问题

错误消息解释如下:

<interactive>:1:5: error:
    • Couldn't match type: [b0]
                     with: [a2] -> t
      Expected: (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
        Actual: (a0 -> b0) -> [a0] -> [b0]
    • In the first argument of ‘sum’, namely ‘map’
      In the expression: sum map (+ 1) [1, 2, 3]

这个错误消息意味着你的代码中存在类型不匹配的问题。具体来说,错误消息的内容如下:

  • sum 函数的第一个参数中,期望的类型是 (a0 -> b0) -> (a1 -> a1) -> [a2] -> t,但实际上你提供了 map,其类型为 (a0 -> b0) -> [a0] -> [b0],这两者不匹配。
  • 错误消息中提到了期望的类型和实际的类型之间的不匹配。
  • 最后,它指出了错误发生的地方,即在 sum 函数的第一个参数中。

要修复这个错误,你需要将 map (+1) [1,2,3] 放在括号内,如下所示:

sum (map (+1) [1,2,3])
英文:

I am learning the basic of Haskell and have trouble understanding compiler messages, that is how do types from the error messages correspond to the types from the expression.

If I intentionally give the compiler the wrong expression

sum map (+1) [1,2,3]   -- this is wrong on purpose to trigger a
                       -- compiler error

instead of the correct

sum (map (+1) [1,2,3])

then I would like to understand what the compiler output means.

For example:

:t sum map (+1) [1,2,3]
&lt;interactive&gt;:1:5: error:
    • Couldn&#39;t match type: [b0]
                     with: [a2] -&gt; t
      Expected: (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
        Actual: (a0 -&gt; b0) -&gt; [a0] -&gt; [b0]
    • In the first argument of ‘sum’, namely ‘map’
      In the expression: sum map (+ 1) [1, 2, 3]

答案1

得分: 3

你想要将 sum 应用于其余部分的结果,所以你需要使用括号,

sum (map (+1) [1,2,3])

或者 a $ 这将首先评估其 rhs,然后再传递给 lhs:

sum $ map (+1) [1,2,3]

你的写法使得你将 sum 应用于 map,就像你写成 (sum map) (+1) [1,2,3] 一样,这是没有意义的。

错误简单地告诉你有一个错误

      sum 的第一个参数中,即 map

其中 (map) 具有 Actual 类型 (a0 -&gt; b0) -&gt; [a0] -&gt; [b0],而编译器能够使整个表达式有意义的唯一方式是如果你传递了 Expected 类型的实体,即 (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t,而不是 map

这个期望的类型只是进一步传递给 sum map 的参数,实际上,sum map 本身在原则上是“正常的”,从类型上看它是

:: (Foldable ((-&gt;) (a -&gt; b)), Num ([a] -&gt; [b])) =&gt; [a] -&gt; [b]

你可以看到预期的类型为 [a] -&gt; [b],它是 Num 的一个实例,这已经表明了期望一个 [a] -&gt; [b],这是 Num 的一个实例。

但是这没关系,因为类型仍然是多态的,不足以约束编译器,以便它可以正常工作。

当你传递了一些类型,它们与 [1,2,3] 几乎完全相同,即 Num a =&gt; [a],以及 (+1),它是 Num a =&gt; a -&gt; a,那么编译器无法理解它,然后告诉你有错误。

你的问题,在某种程度上,可以重新表述为

为什么 \x -&gt; sum x (+1) [1,2,3]x 的类型是 (Foldable t1, Num a1, Num a2, Num ((a1 -&gt; a1) -&gt; [a2] -&gt; t2)) =&gt; t1 ((a1 -&gt; a1) -&gt; [a2] -&gt; t2)

答案仍然是编译器可以做的最多,以尝试使类型匹配。

此外,看一下这个例子,

sum (:) (+1) [1,2,3]

在这里,我将 (:) 传递给 map。错误略有不同:

&lt;interactive&gt;:37:5: error:
     无法匹配类型: [a0]
                     : [a2] -&gt; t
      期望: a0 -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
        实际: a0 -&gt; [a0] -&gt; [a0]
      sum 的第一个参数中,即 (:)
      在表达式中: sum (:) (+ 1) [1, 2, 3]

看看它与你的错误信息相比:

&lt;interactive&gt;:1:5: error:
     无法匹配类型: [b0]
                     : [a2] -&gt; t
      期望: (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
        实际: (a0 -&gt; b0) -&gt; [a0] -&gt; [b0]
      sum 的第一个参数中,即 map
      在表达式中: sum map (+ 1) [1, 2, 3]

在你的例子中,编译器期望的是 (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t,而在我的例子中,它期望的是 a0 -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t。那么编译器是否期望不同的东西?这些类型真的不同吗?

(a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
a0         -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t

不,只是前者限制了第一个参数的类型为 a0 -&gt; b0,即一个函数,而后者可以接受任何 a0,即函数加上非函数。

这是你传递了 map,它的类型是 (a -&gt; b) -&gt; [a] -&gt; [b],而我传递了 (:),它的类型稍微更通用,a -&gt; [a] -&gt; [a],所以前者最终会更加约束 sum 的第一个参数的期望签名,即 (a0 -&gt; b0) 而不是 a0

英文:

You want to apply sum to the result of all the rest, so you need either parenthesis,

sum (map (+1) [1,2,3])

or a $ which evaluates its rhs first before passing it to the lhs:

sum $ map (+1) [1,2,3]

The way you've written it, you're applying sum to map, just as you had written (sum map) (+1) [1,2,3], which doesn't make sense.


The error is simply telling you what there's an error

    • In the first argument of ‘sum’, namely ‘map’

which (map) has the Actual type (a0 -&gt; b0) -&gt; [a0] -&gt; [b0] whereas the only way the compiler could make sense of the whole expression is if you had passed, instead of map, an entity of the Expected type (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t.

That expected type is simply a consequence of the further aruments you've passed to sum map. Indeed, sum map by itself is "fine", in the sense that it could in principle make sense. It's type is

:: (Foldable ((-&gt;) (a -&gt; b)), Num ([a] -&gt; [b])) =&gt; [a] -&gt; [b]

where you can already see the absurdity of expecting a [a] -&gt; [b] which is an instance of Num.

But that's ok, because the types are still polymorphic, not constrained enough for the compiler to work out that thing can't work.

At the moment you pass some types which have almost an exact type such as [1,2,3], which is a Num a =&gt; [a], and (+1), which is Num a =&gt; a -&gt; a, then the compiler can't make head or tail of it, and tells you that.


Your question, in some way, could be rephrased as

> Why the type of x in \x -&gt; sum x (+1) [1,2,3] is (Foldable t1, Num a1, Num a2, Num ((a1 -&gt; a1) -&gt; [a2] -&gt; t2)) =&gt; t1 ((a1 -&gt; a1) -&gt; [a2] -&gt; t2)?

And the answer is again is that that is as much as the compiler can do to try making types check.


Also, look at this example,

sum (:) (+1) [1,2,3]

where I'm passing (:) instead of map. The error is slightly different:

&lt;interactive&gt;:37:5: error:
    • Couldn&#39;t match type: [a0]
                     with: [a2] -&gt; t
      Expected: a0 -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
        Actual: a0 -&gt; [a0] -&gt; [a0]
    • In the first argument of ‘sum’, namely ‘(:)’
      In the expression: sum (:) (+ 1) [1, 2, 3]

See how it compares to yours:

&lt;interactive&gt;:1:5: error:
    • Couldn&#39;t match type: [b0]
                     with: [a2] -&gt; t
      Expected: (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
        Actual: (a0 -&gt; b0) -&gt; [a0] -&gt; [b0]
    • In the first argument of ‘sum’, namely ‘map’
      In the expression: sum map (+ 1) [1, 2, 3]

In your example the compiler expected (a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t, whereas in my example it expected a0 -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t. So was the compiler expecting different things?! Are those types truly different?

(a0 -&gt; b0) -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t
a0         -&gt; (a1 -&gt; a1) -&gt; [a2] -&gt; t

No, it's just that the former is restricted to take a first argument of type a0 -&gt; b0, i.e. a function, whereas the latter can accept any a0, i.e. functions plus non functions.

And that's a consequence of the fact that you you passed map which is a less generic type, (a -&gt; b) -&gt; [a] -&gt; [b], and I passed (:) which is a bit more generic, a -&gt; [a] -&gt; [a], so the former ended up constraining the expected signature of the first argument to sum a bit more, i.e. (a0 -&gt; b0) instead of a0.

huangapple
  • 本文由 发表于 2023年3月31日 16:47:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75896557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定