很明显,任何 n 元组都可以用一堆嵌套的二元组来表示。那么在 Haskell 中它们为什么不是同一件事呢?这样会破坏什么吗?
使这些类型等价将使得在元组上编写函数变得更加容易。例如,你可以只定义一个 zip 函数,而不是定义 zip、zip2、zip3 等等多个函数来处理不同的元组。
当然,你可以使用嵌套的二元组,但这样做很丑陋,并且没有一种标准的嵌套方式(即应该向左还是向右嵌套?)。
很明显,任何 n 元组都可以用一堆嵌套的二元组来表示。那么在 Haskell 中它们为什么不是同一件事呢?这样会破坏什么吗?
使这些类型等价将使得在元组上编写函数变得更加容易。例如,你可以只定义一个 zip 函数,而不是定义 zip、zip2、zip3 等等多个函数来处理不同的元组。
当然,你可以使用嵌套的二元组,但这样做很丑陋,并且没有一种标准的嵌套方式(即应该向左还是向右嵌套?)。
类型(a,b,c,d)
与(a,(b,(c,(d,()))))
的性能特征不同。一般情况下,对n元组的索引需要O(1)
,而对n个嵌套元组的“hlist”进行索引需要O(n)
。
话虽如此,您应该查看Oleg关于HLists的经典作品。使用HLists需要广泛且有些棘手的类型级编程。许多人认为这是不可接受的,并且在早期的Haskell中不可用。今天表示HList最好的方法可能是使用GADTs和DataKinds。
data HList ls where
Nil :: HList '[]
Cons :: x -> HList xs -> HList (x ': xs)
这样做可以实现规范的嵌套,并使您编写的函数适用于此类型的所有实例。您可以使用与printf中使用的技术相同的技术来实现多路zipWith
。生成此类型的适当镜头是一个更有趣的难题(提示:使用类型级自然数和类型族进行索引)。
我考虑过编写类似于HList的库,该库在底层使用数组和unsafeCoerce
以获得类似元组的性能,同时保持通用界面。我还没有完成它,但它不应该过于困难。
编辑:我越想越倾向于在有时间时拼凑出一些东西。使用流融合或类似技术可能可以消除Andreas Rossberg提到的重复复制问题。
a:b:c:[]
?如果这是你关心的问题,我们可以为值级别设计更好的构造函数。 - Philip JF(a,(b,())
包含所有形如 (x,_|_)
或 (x,(y,_|_))
的值,但平面元组却不是这样。这些值的存在不仅在语义上不方便,而且也会使元组更加难以优化。(a,!(b,!(c,!()))) ~ (a,b,c)
,会怎么样?此外,所有这些复制操作不是可以在编译时而不是运行时完成吗? - Mike Izbicki<=
实际数组大小可能要大得多,或者通过指数调整大小增长),则复制只需复制这两个字段。实际数组可以共享,我想。 - Will Ness