【问题标题】:Why are GHC tuples limited to size 62?为什么 GHC 元组的大小限制为 62?
【发布时间】:2018-03-06 20:56:45
【问题描述】:

来自Haskell 98 report

元组的大小没有上限,但一些 Haskell 实现可能会限制元组的大小,并限制与较大元组关联的实例。然而,每个 Haskell 实现必须支持最大 15 的元组,以及 Eq、Ord、Bounded、Read 和 Show 的实例。 (...)

但是,众所周知,GHC 不支持大小大于 62 的元组。以下是我尝试在 GHCi 中创建大小为 63 的元组时发生的情况:

<interactive>:1:1: error:
    A 63-tuple is too large for GHC
      (max size is 62)
      Workaround: use nested tuples or define a data type

我知道这符合 Haskell 98 规范,而且大小大于 62 的元组可能是非常不必要的,但我不明白为什么在 GHC 中就是这样。


总结一下:

  • 为什么有元组大小限制?
  • 为什么大小限制专门为 62?

另外:

  • 为什么只有 GHC 6.12.2 及更高版本才会出现这种情况?
  • 其他著名的 Haskell 实现是否这样做?他们的原因是什么?

【问题讨论】:

  • 高基数元组在 Haskell 中并不经常使用,因为我们更喜欢使用结构(共)递归数据类型。我想不出我曾经使用过大于 3 元组的任何东西。这样做将是代码气味恕我直言。 GHC 的建议也是有效的,如果您真的需要超过 62 个字段的产品类型,那么值得自己定义一个 data 记录。
  • 为什么有大小限制,为什么是62?因为Manuel Chakravarty says 较大的元组会导致分段错误。早在 GHC 6 中,此限制不存在。弄清楚从那以后发生了什么会很有趣......
  • @AJFarmar 我认为 Alec 的评论包含比“此限制从 GHC 6.12.2 开始”更多的内容——它也是对您提出的问题的直接回答。 “为什么有元组大小限制?”为了避免段错误。 “为什么限制是62?”因为那是段错误开始的地方。 (事实上​​,我认为他应该将其发布为答案而不是评论!)
  • 我在这里发现有趣的是,记录可以轻松拥有超过 62 个类型变量/字段。所以这意味着有一些核心方式,元组实际上不是我们总是被告知它们是的记录类型。这让我想知道(1)为什么和(2)我如何利用这种差异?
  • 事后证明:有很多样板代码实例为所有元组大小(如Data.Binary)创建类实例,导致代码大小为 $O(n^2)$其中 $n$ 是最大元组大小。

标签: haskell compiler-errors tuples size compiler-specific


【解决方案1】:

我认为猜测是:cmets 发生这种变化的时机是错误的。首先,据我所知,从 6.12.1 之前的 LONG 开始就存在限制。从Trac #98 from November 2002 可以看出,在 5.02.2 版本中,限制为 37(而不是 62),尝试使用更大的元组会产生一条神秘消息:

Switches_tupel.lhs:1:
Failed to find interface decl for
`PrelTup.(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)'
    from module `PrelTup'

Simon Peyton-Jones 通过让编译器在编译管道的早期检查大小并生成更好的错误消息(在 Git 提交 b44c6881 中可见)修复了该错误。到提交提交时,限制已经从 37 增加到 62(Git 提交 9af77fa4,它将 Template Haskell 工作集成到 HEAD 中),因此 GHC 5.04 发布时具有 62 元组限制和更好的错误消息。

我相信最初的 Trac #98 错误指出了限制的原因。在ghc/compiler/prelude/TysWiredIn.hs 中,预先分配了一组元组类型和数据构造函数:

boxedTupleArr, unboxedTupleArr :: Array Int (TyCon,DataCon)
boxedTupleArr   = listArray (0,mAX_TUPLE_SIZE) [mk_tuple Boxed   i 
                    | i <- [0..mAX_TUPLE_SIZE]]
unboxedTupleArr = listArray (0,mAX_TUPLE_SIZE) [mk_tuple Unboxed i 
                    | i <- [0..mAX_TUPLE_SIZE]]

mAX_TUPLE_SIZE 是有问题的 62 元组限制。但是,实际使用这些预分配数组的函数很乐意按需生成更大的构造函数(“专门构建一个”):

tupleTyCon :: Boxity -> Arity -> TyCon
tupleTyCon sort i | i > mAX_TUPLE_SIZE 
                = fst (mk_tuple sort i)  -- Build one specially
tupleTyCon Boxed   i = fst (boxedTupleArr   ! i)
tupleTyCon Unboxed i = fst (unboxedTupleArr ! i)

这是编译器在 Simon 添加 5.04 的错误消息之前所做的——它专门构建了一个。

不幸的是,当编译器在ghc/libraries/ghc-prim/GHC/Tuple.hs 中给出的列表中找不到太大的元组的接口定义时,这会导致编译过程后期出现错误(不是段错误,只是错误)。根据TysWiredIn.hsThe tuple types 标题下的(稍微过时的)cmets,Tuple.hs 中的声明用于构造元组构造函数的信息表和入口代码,即使这些理论上可以通过编程方式即时生成用于任意大的元组。

那么,这对现代 GHC 意味着什么?好吧,出于与上述相同的技术原因,即使编译器准备生成任意大的元组,由于它们需要匹配 .../GHC/Tuple.hs 中的声明这一事实施加了限制。

我进行了一些实验,在禁用元组长度检查的情况下从源代码编译 GHC。生成的编译器成功编译并运行了以下包含 100 个元组的程序:

a = (False,...,False)  -- imagine 100 Falses
main = let (x,_,...,_) = a
       in print x

它打印出“False”。当我修改它以获取同一元组的最后一个元素时,它运行良好:

a = (False,...,False)  -- imagine 100 Falses
main = let (_,...,_,x) = a
       in print x

但是,程序:

a = (False,...,False)  -- imagine 100 Falses
main = let (x,_,...,_,y) = a
       in print (x,y)

因链接错误而失败:

[1 of 1] Compiling Main             ( Tuple.hs, Tuple.o )
Linking Tuple ...
Tuple.o(.data+0x0): error: undefined reference to 'ghczmprim_GHCziTuple_Z100T_con_info'
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)

我怀疑对于前两个程序,编译器优化了对丢失构造函数的引用,但最终程序需要它。在我在Tuple.hs 中添加一个 100 元组的声明并重建编译器后,所有三个程序都编译并运行良好。

简而言之,在Tuple.hs 中编译手动构建的元组列表会生成所需的数据结构以支持最大 62 大小的元组,并且没有人有足够的动力重新实现此数据结构生成以独立于 @ 987654339@拐杖。如果他们这样做了,GHC 可能会支持任意大小的元组。

顺便说一句,Tuple.hs 中关于 Manuel 的段错误(在此问题的一个 cmets 中引用)的注释可追溯到 2001 年 7 月,当时它被检查到 libraries/base/Data/Tuple.hs,所以无论它是关于什么的,它什么都没有与 GHC 6.12.1 相关。这个问题可能是 Simon 将最大值设置为 62 的原因,但该限制似乎不再适用于现代 GHC。

【讨论】:

  • 有趣。解除元组大小的限制听起来不应该难以做到。我可能会尝试这样做,就像玩弄 GHC 内部一样。
  • 太棒了。我希望有一天你的工作能被收录在一本关于 GHC 历史的书中。 :D
  • 我不确定这是否如此简单。问题是您需要 一个 的地方来声明必要的信息表。如果您在每个需要它们的模块中重新生成它们,那么各种事情都会中断(例如分析)。我想您也许可以依靠链接器来消除重复的表,但这不是我们目前要做的事情。
猜你喜欢
  • 1970-01-01
  • 2023-03-08
  • 2017-09-19
  • 2019-01-21
  • 2022-07-21
  • 1970-01-01
  • 2013-11-29
  • 2011-10-02
  • 1970-01-01
相关资源
最近更新 更多