数据族实例:新类型、数据



对于 Haskell 98 decls,整个数据类型必须是newtypedata。但是数据族可以混合使用newtype instancedata instance。这是否意味着newtype是数据构造函数的属性而不是数据类型?会不会有一个哈斯克尔,比如:

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 ...内移动这两个构造函数十进制来解决这个问题Bar [a].但随后MkBar2成为GADT(因为它的结果类型不是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

哼?MkBar1是一个 newtype 构造函数,其类型不是该形式。

如果可能的话,请不要深入谈论角色:我试图理解它们;这只会让我头疼。讨论这些构造函数的构造和模式匹配。

您可以将数据族视为(单射和开放(类型族的更强版本。从我刚才问的这个问题中可以看出,数据族几乎可以用注入型族伪造。

这是否意味着 newtype 是数据构造函数的属性而不是数据类型?会不会有一个哈斯克尔,比如:

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

这些是等效的:

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 re 独立新类型Baz中的错误消息具有误导性

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

(大概可以追溯到有数据家族之前(。newtype 构造函数的返回类型必须与实例头完全相同(如果使用 GADT 语法,则为模 alpha 重命名(。对于独立的 newtype,"实例头"表示newtype头。

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

在data/newtype实例头中声明的类型(在文献中被称为"类型方案","monotype" - 因为没有约束,"无函数类型" - 在2008年的论文"使用开放类型函数进行类型检查"中(是所有构造函数的返回类型必须统一的主要类型。(可能没有任何构造函数完全具有该返回类型。

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

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

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

让我感到震惊的是,为了更精确的类型/更好的文档,您可能希望将 newtype 声明为只有一个实例的数据系列,其头部比您必须为独立的 newtype 放置的内容更具体。

最新更新