具有类型类约束构造函数的多态ADT

时间:2019-01-18 15:21:24

标签: haskell

为什么以下代码无法编译?

{-# 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

是否可以做这样的事情?进行编译需要哪些扩展(如果有)?

1 个答案:

答案 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。但是存在存在的有效用法,它们的普遍性比人们期望的要少得多。