当我尝试创建一个返回Thing a
的函数(其中Thing
是GADT)时,我正在与类型检查器进行斗争。一个极少人为的例子:
{-#LANGUAGE GADTs, EmptyDataDecls #-}
module Main where
-- Define a contrived GADT
data TFoo
data TBar
data Thing a where
Foo :: Int -> Thing TFoo
Bar :: String -> Thing TBar
combine :: [Thing a]
combine = [Foo 1, Bar "abc"]
main :: IO ()
main = undefined
typechecker对a
与TBar
不匹配感到不满。据推测,这是因为它已经推断出a
是TFoo
。但是,这是令人惊讶的,因为您可以使用常规总和类型:
data Thing = Foo Int | Bar String
combine :: [Thing]
combine = [Foo 1, Bar "abc"]
无论如何都要在GADT参数上返回类型参数吗?
在人为例子的上下文之外,我需要GADT,所以我可以输入某些函数只采用Foo
,但在此之前我还需要能够返回{{1}的列表}第
答案 0 :(得分:3)
你的量词已经混淆了。
combine :: [Thing a]
表示(forall
隐含在语法中)
combine :: forall a. [Thing a]
基本上,无论combine
是什么,[Thing a]
都必须是a
,因为a
是由调用combine
的代码选择的。或者,在另一种意义上,
-- psuedo-Haskell
combine :: (a :: *) -> [Thing a]
combine
是一个函数,它接受一个类型作为参数,并承诺构造该类型的Things
列表。 combine
具有此类型签名的唯一可能定义是combine = []
,还有一些像[undefined]
等愚蠢的定义,例如。 combine = [Foo 1]
也不起作用,因为a
未被推断,因为设置combine
的{{1}}不是a
;这是用户。
你想要
-- psuedo-Haskell
combine :: [exists a. Thing a]
表示“combine
是一个事物列表,每个事物都是某种未知类型的Thing
”(并且每个Thing
可以是不同的类型)。 exists
量词是forall
的另一面。这意味着combine
的定义可以设置它想要的任何类型,用户将不得不处理它。 Haskell不支持exists
开箱即用,因此您需要定义一个中间数据类型:
data SomeThing = forall a. SomeThing (Thing a)
使用通用量词来创建存在量化的语法有点落后,但想法是你得到
SomeThing :: forall a. Thing a -> SomeThing
基本上删除了a
的知识。
然后你可以
combine :: [SomeThing]
combine = [SomeThing $ Foo 1, SomeThing $ Bar "abc"]