又一个newtype vs. data(风格问题)

8
我非常了解data, newtypetype之间的区别。我正在编写一个小脚本,将构建某种语法树。几乎所有类型都有一个构造函数。为了强制安全性(在Haskell中,多个“不同”的类型可能最终具有相同的类型),我避免使用type。在这种情况下,我不关心懒惰/严格性,也不关心性能(这部分绝不是关键性能)。我主要关注风格。 我有三个选择:
  1. 只使用data。这感觉还可以,但我有许多只有一个参数的构造函数。代码看起来有些浪费...虽然我不在意性能增益,但这样做感觉就不对。
  2. 只使用newtype。这会导致在多个参数的情况下,元组变得非常丑陋。
  3. 混合使用datanewtype,这样看起来不太一致并且稍微有点烦人。我宁愿所有类型以单一一致的方式声明。
我在1和3之间徘徊选择。
2个回答

11

在这种情况下,我会普遍使用data,有几个原因。首先,为了与多参数情况保持一致(应该是 肯定的 data,而不是newtype)。

其次,最重要的是,newtypedata具有不同的语义!一个newtype的构造函数是严格的,而data的构造函数是非严格的,除非你明确地使用严格字段。即使你不关心严格性,或者你的data的所有字段都是严格的,仍然存在一些微妙的差异

我认为单构造函数、单参数data类型并不浪费——从语法上讲,它们和newtype一样轻巧,从语义上讲,对我来说似乎更重要。

您说您不关心性能,但是如果运行时装箱开销对于 data 是真的不方便,那么您可以混合使用它们,只要您知道语义上的区别。然而,如果您使用 -funbox-strict-fields,那么 GHC 可能会为您优化掉单构造函数、单参数 data,如果它们出现在其他数据类型的严格字段中。
通常情况下,当您包装一个现有类型以实现编译时安全性/抽象或定义自己的实例时,应使用 newtype,并且只有当类型恰好由单个字段组成而不是包装时,才应使用 data

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 _|_之间的差异(因为所有值都已完全定义)。

还有一件事要注意:声明的统一性是需要谨慎考虑的。 这表明您在程序中没有对抽象级别进行编码,而是将其留下隐含的。 尝试编码该级别,直到没有剩余可利用的并行声明结构。 这并非总是可能的,但请尽力接近。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接