很明显,任何 n 元组都可以由一堆嵌套的 2 元组表示。那么为什么它们在 Haskell 中不一样呢?这会破坏什么吗?
使这些类型等效将使在元组上编写函数变得更加容易。例如,您可以只定义一个适用于所有元组的 zip 函数,而不是定义 zip、zip2、zip3 等。
当然,您可以使用嵌套的 2 元组,但它很难看,并且没有执行嵌套的规范方法(即我们应该嵌套在左侧还是右侧?)。
该类型(a,b,c,d)
具有与 不同的性能配置文件(a,(b,(c,(d,()))))
。通常,索引到 n 元组需要O(1)
,而索引到 n 个嵌套元组的“hlist”需要O(n)
。
也就是说,您应该查看 Oleg 关于HLists的经典作品。使用 HLists 需要大量使用类型级编程,而且有些粗略。许多人认为这是不可接受的,并且在早期的 Haskell 中是不可用的。今天表示 HList 的最佳方式可能是使用 GADT 和 DataKinds
data HList ls where
Nil :: HList '[]
Cons :: x -> HList xs -> HList (x ': xs)
这提供了规范的嵌套,并允许您编写适用于此类型的所有实例的函数。您可以使用与printfzipWith
中使用的相同技术来实现您的多方式。一个更有趣的难题是为这种类型生成适当的镜头(提示:使用类型级别的自然和类型族进行索引)。
我考虑过编写一个类似 HList 的库,该库使用数组并unsafeCoerce
在底层获得类似元组的性能,同时坚持通用接口。我没做过,但应该不会太难。
编辑:我想得越多,当我有时间的时候,我就越倾向于一起破解一些东西。Andreas Rossberg 提到的重复复制问题可能可以使用流融合或类似技术来消除。
Haskell 中的主要问题是嵌套元组由于懒惰而允许附加值。例如,类型(a,(b,())
由 all (x,_|_)
or占据(x,(y,_|_))
,而扁平元组则不是这种情况。这些值的存在不仅在语义上不方便,还会使元组更难优化。
不过,用严格的语言来说,您的建议确实是一种可能性。但它仍然引入了一个性能缺陷:实现仍然希望扁平化元组。因此,在您实际以归纳方式构造或解构它们的情况下,它们将不得不进行大量重复复制。当您使用非常大的元组时,这可能是个问题。