【问题标题】:Haskell "Parse error in pattern" when deriving an instance from Eq从 Eq 派生实例时 Haskell“模式中的解析错误”
【发布时间】:2012-08-26 14:08:16
【问题描述】:

我在“Learn You a Haskell for Great Good!”的帮助下学习 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,在该文件中我尝试为我的 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.

(我必须承认我对这里的正确语法感到很困惑——是item a 还是item? 仅使用 item 失败并出现相同的错误,并且使用更多括号 - 正如 SO 上另一个类似问题的答案一样 - 似乎也无济于事。)

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

【问题讨论】:

    标签: haskell parse-error


    【解决方案1】:

    模式通常以构造函数开始。 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 中的模式可以嵌套;因此,例如,如果您想匹配都具有BellPeppers 的ItemSlots,您可以编写

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

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

    【讨论】:

    • 感谢您的帮助,您的第一个示例为我做了(我只想比较相同的项目,而不是相同的数字)。我想我自己永远也想不通。
    • 只是补充一点:您可以右侧使用item访问器函数(或任何其他函数)定义。所以instance Eq ItemSlot where is1 == is2 = item is1 == item is2 也可以。不过,模式匹配方式可能更好。
    • @Antal S-Z:这正是我最初认为最好的方法,但显然我在写答案时分心了。我已经编辑了我的答案,但赞成你的评论。谢谢提醒。
    【解决方案2】:

    模式匹配在 TrafficLight 示例中有效,因为您 只需要知道构造函数是什么(RedGreenYellow) 是要告诉他们是否相等,但你的ItemSlots 只是 如果 item 字段中的 data 相等,则相等,因此您需要检查 右边有一个方程:

    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,它不会完成,因为它遇到了未定义的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-23
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      • 2015-03-14
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多