构造函数中的类型约束

时间:2019-10-30 16:57:57

标签: haskell

我正在尝试为游戏理论设计一种普通形式的游戏求解器,并且为了自己的方便起见,我试图使其尽可能通用。我想使用相同的函数来解决零和和非零和游戏,因此我使用以下数据类型:

data Payoffs = (Num a, Eq a, Ord a) => ZS a
    | (Num a, Eq a, Ord a) => NZS (a,a)

但是,这不是正确的语法。有什么方法可以约束a,使其必须满足这些类型约束?

1 个答案:

答案 0 :(得分:4)

简短答案(可能不是您需要的答案):
要使您的代码按原样工作,您需要一个forall量词(需要启用ExistentialQuantification):

{-# LANGUAGE ExistentialQuantification #-}

data Payoffs =
      forall a. (Num a, Eq a, Ord a) => ZS a
    | forall a. (Num a, Eq a, Ord a) => NZS (a,a)

如果数据构造函数中有类型变量(即ZS a),则有两种选择:要么该变量必须出现在类型构造函数中(即data Payoffs a =),要么您需要说“ 我不在乎它是什么类型,只要它支持这些类”即可-通过forall量词来实现。

但这对我来说似乎没有用,这表明您可能误解了它的含义。如果您编写上述代码,则Payoffs类型的每个值都可以包装 any 类型的值,只要该类型支持Num,{{1 }}和Eq。一个细微的后果是,如果您有两个Ord值,它们不一定会包装相同的类型。例如:

Payoffs

这意味着在解压缩它们后,您将无法将它们添加在一起,因为,即使它们都实现了let x = ZS (42 :: Int) -- wraps an Int let y = NZS (2.71 :: Double, 3.14) -- wraps two Doubles ,编译器也没有任何证据证明它们是实际上是同一类型。

我怀疑您实际上需要的是参数化类型,例如:

Num

但是,当然,您失去了约束:任何人都可以创建data Payoffs a = ZS a | NZS (a, a) 之类的东西。您可以使用GADT语法(扩展名为ZS String)将它们带回:

GADTs

此符号等效于{-# LANGUAGE GADTs #-} data Payoffs a where ZS :: (Num a, Ord a, Eq a) => a -> Payoffs a NZS :: (Num a, Ord a, Eq a) => (a, a) -> Payoffs a ,除了您要使用与任何函数(包括约束)相同的语法定义每个构造函数。除非ZS a | NZS (a, a)满足约束,否则这样定义的类型将不允许创建类型Payoffs a的值。

同时,如果您有一个像这样的值,那么您知道它包装在哪种类型中。这样就可以判断两个a值是否包装相同或不同的类型。然后,如果您知道它们相同,则可以使用受支持的类对它们进行处理,例如:

Payoffs