haskell - 值构造函数和元组有什么区别?

标签 haskell tuples algebraic-data-types value-constructor

It's written Haskell 元组只是代数数据类型的不同语法。类似地,也有如何用元组重新定义值构造函数的示例。

例如,Haskell 中的 Tree 数据类型可以写为

data Tree a = EmptyTree | Node a (Tree a) (Tree a)

可以像这样转换为“元组形式”:
data Tree a = EmptyTree | Node (a, Tree a, Tree a)
Node 之间有什么区别?第一个示例中的 value 构造函数,以及实际的 tuple在第二个例子中?即Node a (Tree a) (Tree a)(a, Tree a, Tree a) (除了语法)?

在引擎盖下,是 Node a (Tree a) (Tree a)只是每个位置的适当类型的 3 元组的不同语法?

我知道您可以部分应用值构造函数,例如 Node 5类型:(Node 5) :: Num a => Tree a -> Tree a -> Tree a
你也可以部分应用一个元组,使用 (,,)作为一个函数......但这不知道未绑定(bind)条目的潜在类型,例如:
Prelude> :t (,,) 5
(,,) 5 :: Num a => b -> c -> (a, b, c)

除非,我猜,你用 :: 显式声明了一个类型。 .

除了像这样的语法特性,加上最后一个类型作用域的例子,Haskell 中的“值构造函数”实际上是什么,与用于存储相同类型的位置值的元组是值构造函数之间是否存在实质性区别?论据?

最佳答案

好吧,从概念上讲确实没有区别,实际上其他语言(OCaml,Elm)正是以这种方式呈现标记的联合 - 即,在元组或第一类记录上的标记(Haskell 缺乏)。我个人认为这是 Haskell 的设计缺陷。

虽然有一些实际差异:

  • 懒惰。 Haskell 的元组是惰性的,你无法改变它。但是,您可以将构造函数字段标记为严格:
    data Tree a = EmptyTree | Node !a !(Tree a) !(Tree a)
    
  • 内存占用和性能。绕过中间类型可减少占用空间并提高性能。您可以在 this fine answer 中了解更多信息.

    您还可以使用 the UNPACK pragma 标记严格字段进一步减少占地面积。或者,您可以使用 the -funbox-strict-fields compiler option .关于最后一个,我只是更喜欢在我的所有项目中默认启用它。见 the Hasql's Cabal file例如。


  • 考虑到上述情况,如果您要查找的是惰性类型,则以下代码段应编译为相同的内容:
    data Tree a = EmptyTree | Node a (Tree a) (Tree a)
    
    data Tree a = EmptyTree | Node {-# UNPACK #-} !(a, Tree a, Tree a)
    

    所以我想你可以说可以使用元组来存储构造函数的惰性字段而不会受到惩罚。尽管应该提到这种模式在 Haskell 社区中有点不合常规。

    如果您追求的是严格的类型和占用空间减少,那么除了将元组直接非规范化为构造函数字段之外别无他法。

    关于haskell - 值构造函数和元组有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27476446/

    相关文章:

    java - 如何从 Runtime.getRuntime.exec() 启动 Haskell 进程?并进行互动交流?

    haskell - Haskell 中的代码检测

    Python 使用组合对字典列表中的元组中的值求和?

    具有许多构造函数的用于 ADT 的 Haskell Zipper

    haskell - 如何使用记录设计扩展

    haskell - Haskell中树数据结构的邻接表

    c++ - 将从元组派生的对象放入 C++ 中的 vector 中

    python - 如何测试 X 是否像列表/元组一样嘎嘎作响

    haskell - 在haskell中制作类似网格的数据类型

    haskell - 为大输入返回负数的阶乘函数