这是一个愚蠢的问题,一直困扰着我。为什么我不能写一个包含多个参数的newtype,
newtype A = A Int Int
虽然元组版本很好吗?
newtype A = A (Int, Int)
前者在模式匹配等方面更为出色。
答案 0 :(得分:11)
newtype A = A Int
创建一个与Int
同构的类型。也就是说,它的行为与Int
w.r.t完全相同。例如bottom
,但名称不同。
这与data A = A Int
形成对比,Int
创建了一个与Int不同的提升类型。还有一个不在A undefined
undefined::A
中的增值(与newtype A = A (Int, Int)
不同)。
现在,(Int, Int)
创建了一个与data A = A Int Int
同构的类型。顺便说一句,这恰恰是newtype A = A Int Int
所做的。
因此,如果我们承认newtype A = A (Int, Int)
等同于newtype A = A Int Int
,我们有什么? newtype A = A (Int, Int)
相当于data A = A Int Int
,相当于newtype A = A Int Int
。
data A = A Int Int
相当于newtype
(因此newtype A = A Int
在这种情况下是多余的),但
data A = A Int
不等同于newtype
(这是首先拥有newtype A = A Int Int
的重点。)
因此,我们必须得出结论:newtype A = A (Int, Int)
等同于newtype A = A Int Int
会造成冗余和不一致,我们最好不要允许它。
可能没有办法给{{1}}一些其他没有这些不一致的含义(否则我会发现并使用它;)
答案 1 :(得分:7)
因为粗略地说newtype
在运行时与type
类似,在编译时类似于data
。每个data
定义都会增加一个额外的间接层 - 在正常情况下,这意味着另一个不同的地方可以将某些东西留作thunk - 围绕它所拥有的值,而newtype
则不会。 newtype
上的“构造函数”基本上只是一种幻觉。
任何将多个值组合成一个或者在多个案例之间进行选择的东西,必然会引入一个间接层来表达,因此newtype A = A Int Int
的逻辑解释将是两个断开的Int
值什么都没有“把它们抱在一起”。 newtype A = A (Int, Int)
的区别在于元组本身添加了额外的间接层。
将此与data A = A Int Int
与data A = A (Int, Int)
进行对比。前者在两个A
周围添加了一个层(Int
构造函数),而后者在元组周围添加了相同的层,它本身在Int
周围添加了一个层。 / p>
每一层间接通常也会添加一个可以something的地方,所以考虑每种形式的可能情况,在哪里?代表非最低价值:
对于newtype A = A (Int, Int)
:⊥
,(⊥, ?)
,(?, ⊥)
,(?, ?)
对于data A = A Int Int
:⊥
,A ⊥ ?
,A ? ⊥
,A ? ?
对于data A = A (Int, Int)
:⊥
,A ⊥
,A (⊥, ?)
,A (?, ⊥)
,A (?, ?)
从上面可以看出,前两个是等价的。
最后一点,这里有一个有趣的演示,说明newtype
与data
的区别。考虑这些定义:
data D = D D deriving Show
newtype N = N N deriving Show
哪些可能的价值,包括所有可能的价值,都有?您认为以下两个值是什么?
d = let (D x) = undefined in show x
n = let (N x) = undefined in show x
将它们装入GHC并查找!