从方程式中导出实例时,Haskell“模式中的解析错误”

时间:2012-08-26 14:08:16

标签: haskell parse-error

我正在学习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函数,但我不知道如何解决此问题。

2 个答案:

答案 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示例中起作用,因为您 只需知道构造函数(RedGreenYellow) 是告诉他们是否平等,但你的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 == eg1eg2 == eg4甚至eg2 == eg5 问题 - 延迟评估意味着在检查==时它不会检查数字字段, 但是如果你只是输入eg4eg5,它就不会完成,因为它会遇到未定义的值。