Haskell “哈斯克尔”;模式“中的分析错误”;从Eq导出实例时

Haskell “哈斯克尔”;模式“中的分析错误”;从Eq导出实例时,haskell,parse-error,Haskell,Parse Error,我在“向你学习一个非常好的Haskell!”的帮助下学习Haskell,目前正在尝试理解类型类和实例。 LYAH提供了一个名为TrafficLight的类型定义如下: data TrafficLight = Red | Yellow | Green 现在,TrafficLight应该是显示以下行为的Eq的一个实例: instance Eq TrafficLight where Red == Red = True Green == Green = True Yellow

我在“向你学习一个非常好的Haskell!”的帮助下学习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
,试图覆盖
ItemSlot
Eq
行为

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.
(我必须承认,我对这里的正确语法感到相当困惑——是
项目a
还是仅仅
项目
? 仅使用
失败,并出现相同的错误,并且使用更多的括号——就像在SO上的另一个类似问题中的答案一样——似乎也没有帮助。)


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

模式通常以构造函数开始。
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
s,例如,两者都有
BellPepper
s,您可以编写

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

虽然通常您会将
项目
的比较委托给
项目
Eq
实例。

模式匹配在
交通灯
示例中起作用,因为您 只需要知道构造函数是什么(
红色
绿色
黄色
) 要知道它们是否相等,但您的
ItemSlot
s仅为 如果
字段中的数据相等,则相等,因此需要进行检查 右边有一个等式:

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
正如安泰S-Z提醒我的那样(谢谢)

如果你用

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
,而无需 问题-延迟计算意味着它在检查
==
时不检查数字字段,
但是,如果您只键入
eg4
eg5
,它将不会完成,因为它遇到了未定义的值。

谢谢您的帮助,您的第一个示例为我完成了这项操作(我只想比较相同的项目,而不是相同的数字)。我想我自己永远也不会想到这一点。再加上一点:您可以使用
访问器功能(或任何其他功能)在定义的右侧。因此,
实例Eq ItemSlot,其中is1==is2=item is1==item is2
也会起作用。不过,模式匹配方式可能更好。@Antal S-Z:这正是我最初认为最好的方式,但显然我在写答案时分心了。我已经编辑了答案,但没有更新ted你的评论。谢谢提醒我。