【问题标题】:Type Constraint in Constructor [duplicate]构造函数中的类型约束
【发布时间】:2020-02-26 01:06:48
【问题描述】:

我正在尝试为博弈论制作一个正规形式的博弈求解器,并且我正在尝试使其尽可能通用,以实现良好的实践和我自己的方便。我想使用相同的函数来解决零和游戏和非零和游戏,所以我使用以下数据类型:

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

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

【问题讨论】:

  • 你的意思是写data Payoffs a = ...吗?
  • 完整的旁注:不要NZS (a,a)。而是NZS a a。额外的元组构造函数对您没有任何好处(并且正在做坏事:额外分配和间接)。
  • @DanielWagner 我使用元组是因为它在数学上应该是一个元组
  • @n.m.如果我写data Payoffs a = ...,我还能将Ints 的收益与Fractionals 的收益进行比较吗?
  • 在数学上,你可能会想到作者在他们手稿的开头添加了一个短句,“当我写语法(x, y)作为非零和游戏的回报时,我我将其用作一个漂亮的符号,其实际含义是NZS x y。”。然后您可以随意使用NZS a a 而不是NZS (a,a)。 (毕竟,我敢打赌你已经隐含地为你的其他构造函数想象了一个类似的句子......或者你正在阅读的数学定义真的在整个地方都包含ZS标签......?)

标签: haskell


【解决方案1】:

简答(可能不是您需要的):
要使您的代码按原样工作,您需要一个 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 类型的值,只要该类型支持NumEq 和@987654330 @。这样做的一个微妙结果是,如果您有两个 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)

但是,当然,你失去了约束:任何人都可以去创建ZS String 或其他东西。您可以使用 GADT 语法(带有 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 值是包装相同类型还是不同类型。然后,如果您知道它们是相同的,则可以使用支持的类对它们进行处理,例如:

addPayoffs :: Payoffs a -> Payoffs a -> Payoffs a
addPayoffs (ZS a) (ZS b) = ZS (a + b)
addPayoffs (ZS a) (NZS (x,y)) = NZS (a+x, a+y)
... etc.

【讨论】:

  • 我将使用 GADT 语法。然后将Payoffs 设为NumEqOrd 的实例是否也值得,还是没有必要?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-26
  • 2011-09-10
  • 2011-12-17
相关资源
最近更新 更多