为什么以下代码无法编译?
{-# LANGUAGE GADTs #-}
class Foo x where whoAmI :: x -> Int
data One = One
instance Foo One where
whoAmI _ = 1
data Two = Two
instance Foo Two where
whoAmI _ = 2
data Poly f = (Foo f) => Poly { member :: f }
makePoly :: Bool -> Poly f
makePoly what =
if what == True then Poly { member = One }
else Poly { member = Two }
显然,这是一个人为的示例。最后,我希望有一条记录Poly f
并将其传递给其他函数,例如g :: Poly f -> Int
,其中g
只能通过使用类{{ 1}}。
这是GHC v8.6.3编译错误:
Foo
是否可以做这样的事情?进行编译需要哪些扩展(如果有)?
答案 0 :(得分:1)
您似乎对存在性有些疑惑。你写的是什么
data Poly f = (Foo f) => Poly { member :: f }
定义了一系列类型,例如一种类型Poly One
,另一种类型 Poly Two
等。构造Poly
时,将检查其Foo
-ness。
问题
makePoly :: Bool -> Poly f
是呼叫者选择f
。所以
makePoly True :: Poly One
makePoly True :: Poly Two
makePoly True :: Poly Elephant
所有都必须工作,但是您的功能不能以这种方式工作。似乎您几乎知道自己在做什么,因为如果不是定义一个类型家族而是定义一个 single 存在类型,那么这很好:
data Poly = forall f. (Foo f) => Poly { member :: f }
请注意,f
符号的左侧没有=
,因此这是单一类型,而不是家庭类型。因此,声称返回Poly
的函数可以返回它喜欢的任何Poly
。此定义使您的代码正常工作。
技术说明,member
字段访问器完全没有用,因为返回类型取决于传入的 value
member :: Poly -> ????
,因此无法在Haskell类型系统中为它分配类型(不过,依赖类型系统可以做到)。要使用Poly
,您必须 模式匹配:
usePoly :: Poly -> Int
usePoly (Poly x) = whoAmI x -- x :: a for some *unknown* type a in this scope
另一个说明,Poly
完全等同于Int
,因为如果给我们一个Poly x
,我们对x
所知道的就是它是一个{{1 }},而我们只能使用Foo
来处理Foo
。在这种情况下,我只会skip the existential并使用whoAmI
。但是存在存在的有效用法,它们的普遍性比人们期望的要少得多。