【发布时间】:2020-11-21 01:12:38
【问题描述】:
有很多关于 GADTs 比 DatatypeContexts 更好的问答,因为 GADT 会自动在正确的位置提供约束。例如here、here、here。但有时我似乎仍然需要一个明确的约束。这是怎么回事?示例改编自this answer:
{-# LANGUAGE GADTs #-}
import Data.Maybe -- fromJust
data GADTBag a where
MkGADTBag :: Eq a => { unGADTBag :: [a] } -> GADTBag a
baz (MkGADTBag x) (Just y) = x == y
baz2 x y = unGADTBag x == fromJust y
-- unGADTBag :: GADTBag a -> [a] -- inferred, no Eq a
-- baz :: GADTBag a -> Maybe [a] -> Bool -- inferred, no Eq a
-- baz2 :: Eq a => GADTBag a -> Maybe [a] -> Bool -- inferred, with Eq a
为什么unGADTBag 的类型不能告诉我们Eq a?
baz 和 baz2 在道德上是等价的,但类型不同。大概是因为unGADTBag 没有Eq a,那么约束就不能传播到使用unGADTBag 的任何代码中。
但是对于baz2,Eq a 约束隐藏在GADTBag a 内。大概baz2 的Eq a 会想要字典的副本已经存在(?)
GADT 是否可能有许多数据构造函数,每个构造函数都有不同(或没有)约束?此处不是这种情况,也不是针对 Bags、Sets、Ordered Lists 等受限数据结构的典型示例。
使用DatatypeContexts 的GADTBag 数据类型的等价物推断baz 的类型与baz2 相同。
额外问题:为什么我不能为GADTBag 得到一个普通的... deriving (Eq)?我可以用StandaloneDeriving 得到一个,但这很明显,为什么 GHC 不能为我做呢?
deriving instance (Eq a) => Eq (GADTBag a)
问题又是不是可能还有其他数据构造函数?
(在 GHC 8.6.5 中执行的代码,如果相关的话。)
Addit: 根据@chi 和@leftroundabout 的回答——我觉得这两个都没有说服力。所有这些都给*** Exception: Prelude.undefined:
*DTContexts> unGADTBag undefined
*DTContexts> unGADTBag $ MkGADTBag undefined
*DTContexts> unGADTBag $ MkGADTBag (undefined :: String)
*DTContexts> unGADTBag $ MkGADTBag (undefined :: [a])
*DTContexts> baz undefined (Just "hello")
*DTContexts> baz (MkGADTBag undefined) (Just "hello")
*DTContexts> baz (MkGADTBag (undefined :: String)) (Just "hello")
*DTContexts> baz2 undefined (Just "hello")
*DTContexts> baz2 (MkGADTBag undefined) (Just "hello")
*DTContexts> baz2 (MkGADTBag (undefined :: String)) (Just "hello")
而这两个在编译时给出相同的类型错误 * Couldn't match expected type ``[Char]'* No instance for (Eq (Int -> Int)) arising from a use of ``MkGADTBag'/ ``baz2' 分别 [编辑: 我最初的 Addit 给出了错误的表达式和错误的错误信息]:
*DTContexts> baz (MkGADTBag (undefined :: [Int -> Int])) (Just [(+ 1)])
*DTContexts> baz2 (MkGADTBag (undefined :: [Int -> Int])) (Just [(+ 1)])
所以baz, baz2 在道德上是等价的,不仅因为它们为相同的定义明确的参数返回相同的结果;但也因为它们对相同的定义不明确的参数表现出相同的行为。还是它们仅在报告缺少Eq 实例的地方有所不同?
@leftroundabout 在您实际解构
x值之前,无法知道MkGADTBag构造函数确实适用。
是的:当且仅当MkGADTBag 上存在模式匹配时,才定义字段标签unGADTBag。 (如果该类型有其他构造函数,情况可能会有所不同——尤其是如果它们也有标签 unGADTBag。)同样,未定义/惰性求值不会推迟类型推断。
明确地说,“[不]令人信服”我的意思是:我可以看到我得到的行为和推断类型。我不认为懒惰或潜在的不确定性会妨碍类型推断。我怎么能暴露baz, baz2 之间的差异,这可以解释为什么它们有不同的类型?
【问题讨论】:
-
对于
quux x = const x (x == x)GHC 推断quux :: Eq a => a -> a。unGADTBag要求MkGADTBag要求Eq a与quux相同,无论是否评估任何相等测试或应用Eq字典。那为什么unGADTBag会丢失Eq a?