很明显,任何n元组都可以用一堆嵌套的2元组来表示。那么为什么它们在Haskell中不是一回事呢?这会破坏什么吗?
使这些类型等效将使得在元组上编写函数变得更加容易。例如,您可以只定义一个适用于所有元组的单个zip函数,而不是定义zip,zip2,zip3等。
当然,你可以使用嵌套的2元组,但它很丑陋并且没有规范的方法来执行嵌套(即我们应该嵌套到左边还是右边?)。
答案 0 :(得分:35)
(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)
这给出了规范的嵌套,并允许您编写适用于此类型的所有实例的函数。您可以使用printf中使用的相同技术实现多路zipWith
。一个更有趣的难题是为这种类型生成合适的镜头(提示:使用类型级自然和类型系列进行索引)。
我考虑编写一个类似HList的库,它使用数组和unsafeCoerce
来实现类似性能的元组,同时坚持使用通用接口。我没有这样做,但它不应该过于困难。
答案 1 :(得分:23)
Haskell中的主要问题是嵌套元组由于懒惰而允许其他值。例如,类型(a,(b,())
由所有(x,_|_)
或(x,(y,_|_))
居住,而扁平元组则不是这种情况。这些值的存在不仅在语义上不方便,而且还会使元组更难以优化。
但是,用严格的语言,你的建议确实是可能的。但它仍然引入了性能缺陷:实现仍然希望压缩元组。因此,在您实际构建或解构它们的情况下,它们将不得不进行大量的重复复制。当你使用非常大的元组时,这可能是个问题。