0

私は「LearnYouaHaskell for GreatGood!」の助けを借りて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で使用されているrecord-syntaxが提供する機能は使用できないと思います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両方にsがあるsを一致させたい場合は、次のように記述できます。BellPepper

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

ただし、通常は、の比較をItemsのEqインスタンスに委任しますItem

于 2012-08-26T14:12:53.120 に答える
2

TrafficLightこの例では、コンストラクター(、、または)が等しいかどうかを判断するだけでよいためRedGreenパターンYellowマッチングが機能しましたが、フィールドのデータItemSlotが等しい場合にのみsが等しいため、次のように確認する必要があります。右辺の方程式: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

アンタル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 == eg4eg2 == eg5==eg4eg5

于 2012-08-26T15:10:42.067 に答える