0

我在“Learn You a Haskell for Great Good”的帮助下学习 Haskell!并且目前正在尝试了解类型类和实例。LYAH 提供了一个示例,其中调用的类型TrafficLight定义如下:

data TrafficLight = Red | Yellow | Green

现在TrafficLight应该是Eq显示以下行为的一个实例:

instance Eq TrafficLight where
    Red == Red = True
    Green == Green = True
    Yellow == Yellow = True
    _ == _ = False

为了理解它是如何工作的,我编写了自己的文件Shop.hs,名为我尝试覆盖Eq我的ItemSlot.

module Shop where

type Number = Int

data Item =
          BellPepper
        | Cabbage
        | Carrot
        | Lettuce
        | Onion
        | Potato
        | Tomato
        deriving (Show, Read, Eq)

data ItemSlot = ItemSlot {
        item :: Item,
        number :: Number
        } deriving (Show)

instance Eq ItemSlot where
        ((item a) == (item a)) = True -- line that contains the error
        _ == _ = False

但是,如果我在 GHCi 中加载文件,我会收到以下错误:

Prelude> :l Shop.hs 
[1 of 1] Compiling Shop             ( Shop.hs, interpreted )

Shop.hs:21:11: Parse error in pattern: item
Failed, modules loaded: none.

(我必须承认,我对这里的正确语法感到很困惑——是它item a还是只是item?使用仅item失败并出现相同的错误,并且使用更多括号——就像在 SO 上的另一个类似问题的答案一样——没有似乎也有帮助。)

我的猜测是我无法使用 中使用item的记录语法提供的功能ItemSlot,但是我不知道如何解决该问题。

4

2 回答 2

5

模式通常以构造函数开始。该ItemSlot类型的构造函数是ItemSlot,因此您将使用它:

instance Eq ItemSlot where
    ItemSlot item a == ItemSlot item' a' = -- use item, item', a, and a'

或者,因为您已定义ItemSlot为记录,所以存在所谓的模式记录语法。您可以按名称而不是位置绑定变量:

instance Eq ItemSlot where
    ItemSlot { item = foo, number = a } == ItemSlot { item = foo', number = a' }
        = -- use foo, foo', a, and a'

如果您不介意混淆的机会,您当然可以隐藏名称:

instance Eq ItemSlot where
    ItemSlot { item = item, number = a } == ItemSlot { item = item', number = a' }
        = -- use item, item', a, and a'

为方便起见,Haskell 中的模式可以嵌套;所以,如果你想匹配ItemSlot两个都有BellPeppers 的 s,例如,你可以写

instance Eq ItemSlot where
    ItemSlot BellPepper a == ItemSlot BellPepper a' = True
    -- or, equivalently
    ItemSlot { item = BellPepper } == ItemSlot { item = BellPepper } = True

Item尽管通常您会将's的比较委托给s 的Eq实例Item

于 2012-08-26T14:12:53.120 回答
2

模式匹配在TrafficLight示例中起作用,因为您只需要知道构造函数 ( Red,GreenYellow) 是什么来判断它们是否相等,但是只有当字段中的数据相等时,您的sItemSlot才相等,因此您需要使用右边的方程:item

instance Eq ItemSlot where
        ItemSlot {item=i} == ItemSlot {item=j} = i == j

这相当于

instance Eq ItemSlot where
        ItemSlot i _ == ItemSlot j _  =  i == j

但更具前瞻性,因为如果您添加另一个字段并且不想更改 的含义==,则可以不理会第一个版本。==(您可能会争辩说您应该在添加字段时重新访问,但根据{item =我的经验,使用语法会导致更清晰的错误消息。

最干净的是

instance Eq ItemSlot where 
        i == j  =  item i == item j

正如 Antal SZ 提醒我的那样(谢谢)。

如果你用

eg1 = ItemSlot {item = Carrot, number = 3}
eg2 = ItemSlot {item = Onion, number = 3}
eg3 = ItemSlot {item = Onion, number = 42}
eg4 = ItemSlot {item = Carrot, number = undefined}
eg5 = ItemSlot {item = Carrot}

你会发现这eg5会给你一个警告。您可以在使用记录时忽略字段,所以Eq上面的第一个版本很好,但是如果您正在定义记录,Haskell 希望您提供所有数据。

您可以检查,eg4 == eg1甚至eg2 == eg4没有eg2 == eg5问题 - 惰性求值意味着它在检查时不检查数字字段==,但如果您只输入eg4or eg5,它不会完成,因为它遇到未定义的值。

于 2012-08-26T15:10:42.067 回答