为什么(a,b,c,d)不是糖(a,(b,(c,(d,())))?

时间:2013-02-20 06:17:52

标签: haskell types

很明显,任何n元组都可以用一堆嵌套的2元组来表示。那么为什么它们在Haskell中不是一回事呢?这会破坏什么吗?

使这些类型等效将使得在元组上编写函数变得更加容易。例如,您可以只定义一个适用于所有元组的单个zip函数,而不是定义zip,zip2,zip3等。

当然,你可以使用嵌套的2元组,但它很丑陋并且没有规范的方法来执行嵌套(即我们应该嵌套到左边还是右边?)。

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来实现类似性能的元组,同时坚持使用通用接口。我没有这样做,但它不应该过于困难。

编辑:我想的越多,我就越倾向于在有空的时候一起破解。 Andreas Rossberg提到的重复复制问题可能可以通过流融合或类似技术来消除。

答案 1 :(得分:23)

Haskell中的主要问题是嵌套元组由于懒惰而允许其他值。例如,类型(a,(b,())由所有(x,_|_)(x,(y,_|_))居住,而扁平元组则不是这种情况。这些值的存在不仅在语义上不方便,而且还会使元组更难以优化。

但是,用严格的语言,你的建议确实是可能的。但它仍然引入了性能缺陷:实现仍然希望压缩元组。因此,在您实际构建或解构它们的情况下,它们将不得不进行大量的重复复制。当你使用非常大的元组时,这可能是个问题。