我正在学习Haskell的帮助下“为了大好的学习你的Haskell!”我目前正在尝试了解类型类和实例。
LYAH提供example,其中名为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
函数,但我不知道如何解决此问题。
答案 0 :(得分: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
的{{1}},你可以写
BellPepper
虽然通常会将instance Eq ItemSlot where
ItemSlot BellPepper a == ItemSlot BellPepper a' = True
-- or, equivalently
ItemSlot { item = BellPepper } == ItemSlot { item = BellPepper } = True
的比较委托给Item
的{{1}}个实例。
答案 1 :(得分:2)
模式匹配在TrafficLight
示例中起作用,因为您
只需知道构造函数(Red
,Green
或Yellow
)
是告诉他们是否平等,但你的ItemSlot
只是
如果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 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
,它就不会完成,因为它会遇到未定义的值。