【问题标题】:Haskell's head tail init and last in GHCiHaskell 的 head tail init 和 last 在 GHCi
【发布时间】:2016-10-30 15:45:13
【问题描述】:

我有一个关于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

【问题讨论】:

  • 无法删除评论?!
  • 这是一个绝对可怕的错误信息。类型检查器首先遇到了一个更微妙的问题并报告了它而不是真正的点。

标签: list haskell types ghci type-constraints


【解决方案1】:

啊,快点,: 接受一个元素,其余的就像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 的回答,因为他逐步解释了错误消息。非常好!谢谢!

【讨论】:

  • (我认为在这种情况下最好完全避免使用性别语言。我有性别,但不是每个人都有。此外,“女孩”可以被视为对女性的不尊重。)
  • 您可能应该编辑此答案,使其读起来像一个答案。在 GHCi 中写入 :t (:) 会产生 (:) :: a -&gt; [a] -&gt; [a]。但是您将 [a] 作为第一个参数传递给它,将 a 作为第二个参数传递给它。
【解决方案2】:

确实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] -&gt; [[Integer]] -&gt; [[Integer]],它期待 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 - 这是执行 init [1..10]110 部分所必需的
  • Enum [[a]] -- 如果init n :: a,那么要使init n : last n 类型良好,我们必须有last n :: [a],因此n 的第二次出现必须有n :: [[a]];那么Enum 约束需要last [1..10].. 部分
  • Num [[a]]——通过类似的推理,这对于执行 last [1..10]110 部分是必需的

但是这些约束一起很难满足——当然,在PreludeData.List 范围内的列表中没有EnumNum 的实例。所以它会抱怨。

【讨论】:

    【解决方案3】:

    与提供给 GHCi 的单行 sn-ps 相比,在较大的程序中,有更多的类型信息(隐式和显式)允许编译器更好地推断类型。因此,您在 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 =&gt; [a]) 列表,其中类型变量 a 本身必须是 alist,因为您使用了 : last n where 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 列是last n 中的n。它说last n :: Int。所以init n :的类型当init n :: [Int]意味着我们的cons函数类型是(:) :: [Int] -&gt; [[Int]] -&gt; [[Int]]。可是等等! init n : 的参数所需的[[Int]]last n 提供的Int 不匹配!

    【讨论】:

    • 不错的答案!谢谢。然而丹尼尔是3分钟。快点。投赞成票。 :)
    • 没关系。我会从丹尼尔的办公桌上拿几票来弥补。
    猜你喜欢
    • 2023-03-10
    • 2021-03-25
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-06
    相关资源
    最近更新 更多