我对head
,tail
,init
和last
有疑问。
以下适用于GHCi:
Prelude Data.List Data.Char> let n = [1..10] in (head n : tail n)
[1,2,3,4,5,6,7,8,9,10]
正如所料,我得到了整个清单。所以这应该适用于init
和last
,
正确?
Prelude Data.List Data.Char> let n = [1..10] in (init n : last n)
<interactive>:39:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
如果我查看函数的类型签名,那么head
和last
看起来一样 - 它们都返回一个元素。另外init
和tail
看起来
同样,因为他们都返回列表。
Prelude Data.List Data.Char> :info head
head :: [a] -> a -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info tail
tail :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info init
init :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info last
last :: [a] -> a -- Defined in ‘GHC.List’
那么Non type-variable argument in the constraint: Enum [[a]]
意味着什么?
如果我在没有构建新列表的情况下init n
或last n
,我会得到
[1..9]
和10
。
答案 0 :(得分:9)
啊,快照,:
接受一个元素,其余的像x:xs
,而不是列表和最后一个元素,就像在xs:x
中一样。
Prelude Data.List Data.Char> :info :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
data [] a = ... | a : [a] >·-- Defined in ‘GHC.Types’
infixr 5 :
我仍然想知道您如何理解GHCi错误消息,我还是要等待2天才能接受我自己的答案。
编辑:我认为x
和xs
只是按惯例选择的变量名称,而(xs:_)
会匹配头部,但会非常规/混淆地命名。
编辑2:我赞成并接受了Daniel Wagner的回答,因为他一步一步地解释了错误信息。非常好!谢谢!
答案 1 :(得分:6)
head
/ last
和tail
/ init
确实具有相同的类型。因此,如果您只是为last
替换了head
而init
换了tail
,那么您就没有问题:
> let n = [1..10] in last n : init n
[10,1,2,3,4,5,6,7,8,9]
但你没有。您同时交换了和另一个:您将参数的顺序更改为:
。碰巧:
没有采用相同类型的两个参数:
> :t (:)
(:) :: a -> [a] -> [a]
所以这最后一次交换不行!事实上,如果你给n
稍微更具体的类型签名,ghci会给出一个更好的错误:
> let n :: [Integer]; n = [1..10] in init n : last n
<interactive>:1:50:
Couldn't match type ‘Integer’ with ‘[[Integer]]’
Expected type: [[[Integer]]]
Actual type: [Integer]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
这个错误仍然没有100%明确,但我觉得有点令人费解,你可以看到它的抱怨:自init n :: [Integer]
和(:) :: [Integer] -> [[Integer]] -> [[Integer]]
,它&#39;期待last n :: [[Integer]]
,因此n :: [[[Integer]]]
。但是你明确地说n :: [Integer]
是冲突。
现在,它在你的情况下实际给你的错误怎么样?那么,线索的类型为[1..10]
:
> :t [1..10]
[1..10] :: (Enum t, Num t) => [t]
请注意[1..10]
是多态的。此外,它在表达式中使用了两次,因此在两次使用中可以给出单独的单态类型。所以[1..10]
在续集中用两个不同类型进行实例化!
现在我认为你可以开始看到你所遇到的错误。它试图找到一个a
类型:
Enum a
- 这需要执行..
init [1..10]
部分
Num a
- 执行1
10
和init [1..10]
部分时需要这样做
Enum [[a]]
- 如果init n :: a
,那么要init n : last n
输入良好,我们必须last n :: [a]
,因此第二次出现n
必须有n :: [[a]]
;然后,Enum
..
部分需要last [1..10]
约束
Num [[a]]
- 通过类似的推理,这需要执行1
10
和last [1..10]
部分
但这些约束条件难以满足 - 当然Enum
或Num
范围内的列表中没有Prelude
和Data.List
的实例。所以它抱怨。
答案 2 :(得分:2)
在较大的程序中,有更多的类型信息(隐式和显式)允许编译器更好地推断类型,而不是提供给GHCi的单行代码片段。因此,您在GHCi中看到的错误并不代表构建完整程序时通常观察到的错误。
那就是说,这不是你所发布错误的可怕原因:
Prelude> let n = [1..10] in (init n : last n)
<interactive>:8:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
因此,您自己的行中涉及很多多态性。您有一个Num a(Num a => [a]
)的列表,其中类型变量a
必须自己是alist,因为您已使用: last n
last n :: [_] ~ a
。因此,结合列表理解中..
暗示枚举的事实,就是我们如何得到可怕的信息。
让我们看看更简单的情况,我们告诉GHCi我们的列表是[Int]
类型:
Prelude> let n = [1..10] :: [Int] in (init n : last n)
<interactive>:7:44:
Couldn't match type ‘Int’ with ‘[[Int]]’
Expected type: [[[Int]]]
Actual type: [Int]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
啊,好多了。第44列是n
中的last n
。它说last n :: Int
。因此,当init n :
表示我们的cons函数类型为init n :: [Int]
时(:) :: [Int] -> [[Int]] -> [[Int]]
的类型。可是等等! [[Int]]
参数所需的init n :
与Int
提供的last n
不匹配!