为什么此Haskell代码会产生“无限类型”错误?


78

我是Haskell的新手,并且遇到了我无法理解的“无法构造无限类型”错误。

实际上,除此之外,我什至无法找到一个很好的解释该错误的含义,因此,如果您可以超越我的基本问题并解释“无限类型”错误,我将不胜感激。

这是代码:

intersperse :: a -> [[a]] -> [a]

-- intersperse '*' ["foo","bar","baz","quux"] 
--  should produce the following:
--  "foo*bar*baz*quux"

-- intersperse -99 [ [1,2,3],[4,5,6],[7,8,9]]
--  should produce the following:
--  [1,2,3,-99,4,5,6,-99,7,8,9]

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:y:xs) = x:s:y:intersperse s xs

这是尝试将其加载到解释器中的错误:

Prelude> :load ./chapter.3.ending.real.world.haskell.exercises.hs
[1 of 1] Compiling Main (chapter.3.ending.real.world.haskell.exercises.hs, interpreted )

chapter.3.ending.real.world.haskell.exercises.hs:147:0:
Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `intersperse'
Failed, modules loaded: none.

谢谢。

-

这是一些经过更正的代码和用于处理Haskell中“无限类型”错误的一般准则:

更正的代码

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:xs) =  x ++ s:intersperse s xs 

问题是什么:

我的类型签名指出要插入的第二个参数是列表列表。因此,当我对“ s(x:y:xs)”进行模式匹配时,x和y成为列表。但是我将x和y视为元素,而不是列表。

处理“无限类型”错误的准则:

在大多数情况下,遇到此错误时,您已经忘记了要处理的各种变量的类型,并且试图像使用其他变量一样使用变量。仔细查看所有内容是什么类型以及您如何使用它,通常可以发现问题所在。


2
另一个好技巧:显式声明类型。这使编译器需要检查一些东西。
保罗·约翰逊

3
这样就解决了问题,但是为什么编译器会说“无法构造无限类型?”。那是什么意思?如果问题是您试图对不支持这些操作的类型执行操作,那么编译器为什么不这样说呢?
freedrull 2010年

11
问题的结构+1(问题-已更正-问题为-准则)
达卡夫2010年

2
@freedrull:错误的措辞是这样的,因为我告诉编译器类型参数“ a”应“用a的列表”填充。这就像说我的咖啡杯包含一堆我的咖啡杯。这确实是一个没有意义的递归定义(或者至少在Haskell中定义为无效)。因此它告诉我“无法构造无限类型”。事实是,我可以使用基于a的type参数来做其他事情。例如,我可以用“ Maybe a”填充该类型参数。因此,我正在做的事情还可以,但是我做的特定事情却不行。
查理·

1
@freedrull:编译器已经注意到代码执行了很多与值相关的事情a,其中任何一个都可以隔离。因此,它无法指向它们中的任何一个并说“这是该类型不支持的操作”。但是它们加在一起就构成了对类型满足的要求a = [a]。编译器知道这是不可能的,因此它告诉您。它不知道导致该要求的代码的哪些部分是错误的部分;这取决于您想要的代码含义。
2016年

Answers:


35

问题出在最后一个子句中,其中x和y是元素,而它们是列表。这将起作用:

intersperse _ [] = []
intersperse _ [x] = x 
intersperse s (x:y:xs) = x ++ [s] ++ y ++ intersperse s xs

发生无限类型错误是因为:运算符的类型为-> [a]-> [a],而您将其视为[a]-> a-> [a],这意味着[a]必须用a,这意味着a是无限嵌套的列表。这是不允许的(无论如何,这也不是您的意思)。

编辑:上面的代码中还有另一个错误。它应该是:

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:xs) = x ++ [s] ++ intersperse s xs

1
谢谢。我找出了两个,然后回到这里,看到了您的回应,这对我来说是极好的验证。您还比我更好地修复了我的错误。我的错误是它跳过了y和xs之间的分隔符。为了解决这个问题,我引入了另一种模式匹配,例如:intersperse s(x:y:[])= x ++ s:y intersperse s(x:y:xs)= intersperse s [x,y] ++ s:intersperse s xs但是,看起来您无需额外级别即可解决了我的错误。
Charlie Flowers

2
这是我要学习的课程:“遇到“无限类型”错误时,您可能会忘记要处理的类型,因此会执行您本不想做的事情。仔细查看每个变量的类型。是的,通常可以发现问题所在。” 您有什么要添加或更改的内容吗?
Charlie Flowers

那当然是正确的,我对此将保持不变。不允许使用无限类型,因此无限类型错误表示某个函数在某个地方接收到具有错误类型的参数。
RWH

where you treat x and y as elements, while they are lists.他们为什么列出清单,但您没有解释呢?在中x:xsx是一个元素,对不对?我希望它也一样x:y:xs。另外,如果它们是列表,则如何拆分它们,包含多少个元素?我猜一个?
纳瓦兹

6

通常,添加显式类型定义可以使编译器的类型错误消息更有意义。但是在这种情况下,显式键入会使编译器的错误消息更糟。

看看当我让ghc猜测穿插类型时会发生什么:

Occurs check: cannot construct the infinite type: a = [a]
  Expected type: [a] -> [[a]] -> [[a]]
  Inferred type: [a] -> [[a]] -> [a]
In the second argument of `(:)', namely `intersperse s xs'
In the second argument of `(:)', namely `y : intersperse s xs'

这显然指向代码中的错误。使用这种技术,您不必像其他人建议的那样凝视所有内容并认真思考类型。


3

我可能是错的,但看来您正在尝试解决更困难的问题。您的版本intersperse不仅将值散布在数组中,而且还将其展平到一个级别。

ListHaskell中的模块实际上提供了一个散布功能。它放入列表中每个元素之间的值。例如:

intersperse 11 [1, 3, 5, 7, 9] = [1, 11, 3, 11, 5, 11, 7, 11, 9]
intersperse "*" ["foo","bar","baz","quux"] = ["foo", "*", "bar", "*", "baz", "*", "quux"]

我假设这是您想要做的,因为这是我在学习Haskell时教授想要我们做的。我当然可以全力以赴。


1
感谢您的评论。不过,在这种情况下,我确实想将其展平,因为我正在从“真实世界Haskell”第3章的末尾开始练习7。
Charlie Flowers

知道了 如果我有这本书,我会在写之前检查一下。las,我所能做的只是猜测。很高兴您能对它进行排序。:-)
萨米尔·

5
本书的内容可在线免费获得:book.realworldhaskell.org
Stephan202

优秀。已收藏。感谢您提供的链接-我将在明年的Haskell实验室中为您提供帮助,这无疑会在刷牙时派上用场。
萨米尔·塔尔瓦尔

0

我也发现解释了错误的含义。

每次解释器/编译器给我这个错误是因为我使用一些类型参数化的元组作为形式参数。通过删除包含类型变量的函数的类型定义,一切都可以正常工作。

我仍然不知道如何修复它并保留函数类型定义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.