Existentially quantified data constructors喜欢
data Foo = forall a. MkFoo a (a -> Bool)
| Nil
可轻松翻译为GADT:
data Foo where
MkFoo :: a -> (a -> Bool) -> Foo
Nil :: Foo
它们之间是否存在差异:代码与一个而不是另一个编译,或者给出不同的结果?
答案 0 :(得分:9)
它们几乎相同,尽管不是完全相同,具体取决于你打开的扩展名。
首先,请注意,您无需启用GADTs
扩展名即可将data .. where
语法用于存在类型。只需启用以下较小的扩展即可。
{-# LANGUAGE GADTSyntax #-}
{-# LANGUAGE ExistentialQuantification #-}
使用这些扩展,您可以编译
data U where
U :: a -> (a -> String) -> U
foo :: U -> String
foo (U x f) = f x
g x = let h y = const y x
in (h True, h 'a')
如果我们用
替换扩展和类型定义,上面的代码也会编译{-# LANGUAGE ExistentialQuantification #-}
data U = forall a . U a (a -> String)
但是,上面的代码在启用GADTs
扩展程序时不进行编译!这是因为GADTs
也会启用MonoLocalBinds
扩展名,这会阻止编译g
的上述定义。这是因为后一种扩展会阻止h
接收多态类型。
答案 1 :(得分:4)
请注意,GADT样式的语法概括了存在类型(存在量化数据构造函数)。例如,这两个声明是等效的:
data Foo = forall a. MkFoo a (a -> Bool) data Foo' where { MKFoo :: a -> (a->Bool) -> Foo' }
(强调等同词)
答案 2 :(得分:4)
后者实际上并不是GADT - 它是使用GADT语法声明的存在量化数据类型。因此,它与前者相同。
它不是GADT的原因是没有根据构造函数的选择而改进的类型变量。这是GADT增加的关键新功能。如果您有这样的GADT:
data Foo a where
StringFoo :: String -> Foo String
IntFoo :: Int -> Foo Int
然后,每个构造函数上的模式匹配会显示可在匹配子句中使用的其他信息。例如:
deconstructFoo :: Foo a -> a
deconstructFoo (StringFoo s) = "Hello! " ++ s ++ " is a String!"
deconstructFoo (IntFoo i) = i * 3 + 1
请注意,从类型系统的角度来看,那里发生了一些非常有趣的事情。 deconstructFoo
承诺只有a
传递Foo a
类型的值,才会对{em>任何选择String
起作用。但是第一个等式返回Int
,第二个等式返回(a ~ String)
。
这是常规数据类型无法做到的,也是GADT提供的新功能。在第一个等式中,模式匹配将约束(a ~ Int)
添加到其上下文中。在第二个等式中,模式匹配会添加export class MyClass {
consultationId: string;
responseType: {
id: string;
name: string;
};
totalResponses: string;
totalNewResponses: string;
totalReviewResponses: string;
totalCompletedResponses: string;
responsesAbstract: {
responseId: string;
consultationId: string;
responseTypeId: string;
respondentId: string;
responseCurrentStatus: string;
};
}
。
如果您还没有创建一种模式匹配可能导致类型细化的类型,那么您就没有GADT。你只有一个用GADT语法声明的类型。这很好 - 在很多方面,它比基本数据类型语法更好的语法。对于最简单的案例,它只是更加冗长。