我正在尝试为游戏理论设计一种普通形式的游戏求解器,并且为了自己的方便起见,我试图使其尽可能通用。我想使用相同的函数来解决零和和非零和游戏,因此我使用以下数据类型:
data Payoffs = (Num a, Eq a, Ord a) => ZS a
| (Num a, Eq a, Ord a) => NZS (a,a)
但是,这不是正确的语法。有什么方法可以约束a
,使其必须满足这些类型约束?
答案 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