【问题标题】:Haskell: Problem with making my own data-type be part of the Eq-typeclassHaskell:使我自己的数据类型成为 Eq-typeclass 的一部分的问题
【发布时间】:2025-12-11 21:50:02
【问题描述】:

我有一个数据类型,声明为:

data Card = Card Suit Value deriving (Show, Eq) 

与:

data Suit = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Show, Eq, Enum, Ord) 

data Value = Spade | Diamond | Club | Heart deriving (Show, Eq, Enum)

我使类型 Card 成为类型类 Eq 的一部分,如下所示:

instance Eq Card where
(Card s1 v1) == (Card s2 v2) = (s1 == s2) && (v1 == v2) 

即使我的解决方案与我们从 uni-class 获得的解决方案完全相同,但我总是从 ghci 收到以下错误:

<interactive>:9:1: error:
    Couldn't match type ‘Card’ with ‘Suit’
    Expected type: Suit -> Suit -> Bool
      Actual type: Card -> Card -> Bool

<interactive>:9:49: error:
    • Couldn't match expected type ‘Suit’ with actual type ‘Value’
    • In the first argument of ‘(==)’, namely ‘v1’
      In the second argument of ‘(&&)’, namely ‘(v1 == v2)’
      In the expression: (s1 == s2) && (v1 == v2)

<interactive>:9:55: error:
    • Couldn't match expected type ‘Suit’ with actual type ‘Value’
    • In the second argument of ‘(==)’, namely ‘v2’
      In the second argument of ‘(&&)’, namely ‘(v1 == v2)’
      In the expression: (s1 == s2) && (v1 == v2)

【问题讨论】:

  • 您的Card 已经是Eqderiving (Show, Eq) 的一个实例,因此您不能再次执行此操作。
  • 我一开始是这样的,但它给了我同样的错误。
  • 但是我无法复制这种行为。您是否自己定义了Eq 类型类(所以class Eq)?在这种情况下,(==) 当然不再是原始Eq 类型类中定义的那个。
  • 请不要。现在有两个Eq 类型类,原来的一个,可能还有SuitValue 作为成员和新的一个。此类已通过Prelude 定义和导入。
  • 解决方案是什么?我知道重新启动终端似乎已经为您修复了它,但这是实际修复还是您的代码也发生了变化?

标签: haskell types typeclass


【解决方案1】:

这个sn-p:

instance Eq Card where
(Card s1 v1) == (Card s2 v2) = (s1 == s2) && (v1 == v2) 

不是一个有效的实例声明。因为您没有缩进定义,所以它在语法上不属于 instance,即编译器看到的只是一个 empty 实例声明(这是语言合法的,尽管实际上不适合Eq;这意味着您将所有方法保留为默认值),另外还有一个名为 == 的运算符的独立定义。该运算符与Eq 类无关,即Prelude.==YourModule.== 将是完全不相关的运算符。通常,这会导致 Ambiguous occurrence ‘==’ 错误,但如果您在 GHCi 中编写此代码,那么您的版本将隐藏前奏,即任何后续使用 == 将引用您的版本。

现在,对于该运算符,编译器从 LHS 推断它必须具有类型

(==) :: Card -> Card -> ...

它在参数类型中不是多态的,就像Prelude.== 一样。

但是,在定义正文中,您使用 s1s2 作为参数调用相同的运算符,这是一个矛盾并导致您发布的错误消息。

你真正想写的是

instance Eq Card where
  (Card s1 v1) == (Card s2 v2) = (s1 == s2) && (v1 == v2) 

instance Eq Card where (Card s1 v1) == (Card s2 v2) = (s1 == s2) && (v1 == v2) 

顺便说一句,括号是完全没有必要的;我推荐这个版本:

instance Eq Card where
  Card s₀ v₀ == Card s₁ v₁ = s₀==s₁ && v₀==v₁

【讨论】: