我非常了解data
,newtype
和type
之间的差异。我正在编写一个小脚本,它将构建某种语法树。 几乎所有类型都有一个构造函数。我正在避免type
强制执行安全性(多个“不同”类型可能最终在Haskell中具有相同的类型)。在这种情况下我不关心懒惰/严格,也不关心性能(这部分绝不是性能关键)。我主要专注于风格。我有三个选择:
data
。这感觉还可以,除了我有很多类型,只有一个带有一个参数的构造函数。代码看起来有些浪费...虽然我不关心性能提升,但它感觉不对。newtype
。在多个参数的情况下,这会导致元组的大量丑陋。data
和newtype
看起来有点不均匀而且有点烦人。我宁愿以一致的方式宣布所有类型。我处于选择1到3之间的两难境地。
答案 0 :(得分:11)
在这种情况下,出于几个原因,我会普遍使用data
。首先,为了与多参数案例保持一致(肯定 data
,而不是newtype
)。
其次,最重要的是,newtype
具有不同的语义到data
!除了明确使用严格字段之外,newtype
的构造函数是严格的,而不是data
的构造函数,它们是非严格的。即使您不关心严格性,或者data
的所有字段都是严格的,仍然有一些subtle differences。
我不认为一个构造函数,一个参数data
类型是浪费的 - 从语法上讲,它们和newtype
一样轻,在语义上,对我来说似乎更重要。< / p>
你说你并不关心性能,但是如果data
的运行时装箱费用非常不方便,那么只要你知道就可以将它们混合起来语义差异。但是,如果您使用-funbox-strict-fields
,那么GHC可能能够为您优化单构造函数,单参数data
,如果它们作为其他数据类型中的严格字段出现。
通常,在包装现有类型时,为了编译时安全/抽象,或者要定义自己的实例,应使用newtype
,并在类型时使用data
恰好是由一个字段组成,而不是一个包装器。
答案 1 :(得分:6)
当我构建没有使用懒惰的微妙事物的真实程序时,我几乎总是将newtype
用于具有单个构造函数和参数和data
的数据类型其他一切:
data Foo = FooA | FooB Int
data Bar = BarA Int Foo
newtype Baz = Baz Bar
至少,如果你发现自己写作
newtype Foo = Foo (X,Y)
语义与相同到
data Foo = Foo X Y
因此您可以使用data
版本,因为它更漂亮。确实
data Foo = Foo Int
newtype Bar = Bar Int
在语义方面有所不同,但最终对“真实”程序不重要,我们不希望知道_|_
和Foo _|_
之间的区别(因为无论如何,所有的值都是完全定义的。)
还有另一件事要看:声明的一致性是警惕的。它表示您没有在程序中编码的抽象级别,您将隐式隐藏。看看是否可以编码该级别,直到没有可用的并行声明结构。这并非总是可行,但试着接近。