数据族实例:newtype,数据

时间:2018-09-20 04:05:00

标签: haskell newtype

使用Haskell 98 decls时,整个数据类型必须为newtypedata。但是数据族可以混合使用newtype instancedata instance。这是否意味着newtype是数据构造函数的属性,而不是数据类型的属性?可能会出现Haskell,例如:

data Foo a = MkFoo1 Int a
           | MkFoo2 Bool a
           | newtype MkFoo3 a

我知道我不能写以下内容,但是为什么/出了什么问题?:

data family Bar a

newtype instance Bar (Maybe Int)  = MkBar1 (Maybe Int)
            -- MkBar1 :: (Maybe Int) -> Bar (Maybe Int), see below

newtype instance Bar [Char]  = MkBar2 [Char]

data instance Bar [Bool]  where
  MkBar3 :: Int -> Bool -> Bar [Bool]
  -- can't be a newtype because of the existential Int

-- we're OK up to here mixing newtypes and data/GADT

data instance Bar [a]  where
  MkBar4 :: Num a => a -> Bar [a]
  -- can't be a newtype because of the Num a =>

我不能这样写,因为实例头Bar [a]MkBar2, MkBar3的两个头重叠。然后,我可以通过在where ...的{​​{1}}内移动这两个构造函数decl来解决此问题。但是随后Bar [a]成为GADT(因为其结果类型不是MkBar2),所以不能成为Bar [a]

然后是newtype的结果类型属性,而不是构造函数的属性吗?但是,请考虑上面针对新类型实例newtype推断的类型。我无法编写具有相同类型的顶级MkBar1

newtype

嗯? newtype Baz a where MkBaz :: (Maybe Int) -> Baz (Maybe Int) -- Error: A newtype constructor must have a return type of form T a1 ... an 是一个新类型构造函数,其类型不是这种形式。

如果可能的话,请在不深入探讨角色的情况下进行解释:我试图理解它们;只会伤到我的头讨论这些构造函数的构造和模式匹配。

2 个答案:

答案 0 :(得分:1)

您可以将数据族视为(内射和开放式)族的更强版本。从this question I asked a while back中可以看出,数据族几乎可以被内射型族伪造。

  

这是否意味着成为newtype是数据构造函数的属性而不是数据类型?可能会出现Haskell,例如:

data Foo a = MkFoo1 Int a
           | MkFoo2 Bool a
           | newtype MkFoo3 a

不。肯定是newtype是类型构造函数的属性,而不是数据构造函数的属性。数据家族与类型家族非常相似,具有两个级别的类型构造函数:

  • 类型构造函数data/type family FamTyCon a
  • 实例data/type instance FamInstTyCon a = ...的类型构造器

同一data/type系列构造函数的不同实例仍然在根本上是不同的类型-它们恰好在一个类型构造函数下统一(并且该类型构造函数将是内射和生成的-有关更多信息,请参见链接的问题)。 )。

根据类型家族的类比,您不会期望能够对TyFam a类型的对象进行模式匹配,对吗?因为您可能拥有type instance TyFam Int = Booltype instance TyFam () = Int,并且无法静态知道自己正在查看Bool还是Int

答案 1 :(得分:0)

这些是等效的:

newtype          NT a b  = MkNT (Int, b)

data family DF a b
newtype instance DF a b  = MkDF (Int, b)

-- inferred MkNT :: (Int, b) -> NT a b
--          MkDF :: (Int, b) -> DF a b

此外,您无法声明族DF中的任何其他实例/构造函数,因为它们的实例头将重叠DF a b

q重新独立的新类型Baz中的错误消息具有误导性

-- Error: A newtype constructor must have a return type of form T a1 ... an

(大概是数据族之前的日期)。新类型的构造函数必须具有与实例头完全相同的返回类型(如果使用GADT语法,则以模数重命名)。对于独立的新类型,“实例头”表示newtype头。

对于非新类型数据实例,各种构造函数的返回类型可能比实例头更严格(这使它们成为GADT)。

在数据/新类型实例头中声明的类型(在文献中被不同地称为“类型方案”,“单型”(因为没有约束,称为“无功能类型”),在2008年论文“具有类型检查”中) “开放类型函数”)是所有构造函数的返回类型必须统一的主体类型。 (可能没有任何具有该返回类型的构造函数。)

如果您的实例是newtype,则规则要严格得多:只能有一个构造函数,并且其返回类型必须恰好是主体类型。所以要回答原来的q

  

这是否意味着newtype是数据构造函数的属性,而不是数据类型的属性?   ...但是...那么newtype是结果类型的属性,而不是构造函数的属性吗?

不,不是构造函数的;是的,更像是结果:作为新类型是数据族实例/其主体类型的属性。只是对于独立的新类型,实际上只能有一个实例,并且其主体类型是该类型构造函数的最通用类型。派生地,我们可以说新类型的主体类型/返回类型唯一地标识数据构造函数。这对于类型安全至关重要,因为@AlexisKing的评论指出,该类型的值与新类型的数据构造函数中的类型共享其表示形式,即没有包装器。然后,模式匹配就不需要去寻找构造函数了:匹配是无可辩驳的/构造函数是虚拟的。

令我惊讶的是,为了获得更精确的键入/更好的文档,您可能希望将一个新类型声明为只有一个实例的数据族,其实例比您为独立的新类型必须放置的头更具体。 / p>