Haskell的头尾init和GHCi的最后一个

时间:2016-06-28 10:47:11

标签: list haskell types ghci type-constraints

我对headtailinitlast有疑问。 以下适用于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]

正如所料,我得到了整个清单。所以这应该适用于initlast, 正确?

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]]

如果我查看函数的类型签名,那么headlast 看起来一样 - 它们都返回一个元素。另外inittail看起来 同样,因为他们都返回列表。

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 nlast n,我会得到 [1..9]10

3 个答案:

答案 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天才能接受我自己的答案。

编辑:我认为xxs只是按惯例选择的变量名称,而(xs:_)会匹配头部,但会非常规/混淆地命名。

编辑2:我赞成并接受了Daniel Wagner的回答,因为他一步一步地解释了错误信息。非常好!谢谢!

答案 1 :(得分:6)

head / lasttail / init确实具有相同的类型。因此,如果您只是为last替换了headinit换了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
  • 10init [1..10]部分时需要这样做
  • Enum [[a]] - 如果init n :: a,那么要init n : last n输入良好,我们必须last n :: [a],因此第二次出现n必须有n :: [[a]];然后,Enum
  • ..部分需要last [1..10]约束
  • Num [[a]] - 通过类似的推理,这需要执行1
  • 10last [1..10]部分

但这些约束条件难以满足 - 当然EnumNum范围内的列表中没有PreludeData.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不匹配!