2012-07-19 10 views
31

における平等の制約は、私がClassyPreludeの発表を通して読んで、ここに着いた。ハスケル:インスタンス

instance (b ~ c, CanFilterFunc b a) => CanFilter (b -> c) a where 
    filter = filterFunc 

作家は、これは動作しないだろうと述べた:

instance (CanFilterFunc b a) => CanFilter (c -> c) a where 
    filter = filterFunc 

に理にかなって私はcが左の制約とはまったく無関係だからです。

しかし、何が資料に記載されていない、これは私が理解していないと動作しないでしょうしている理由:これは、最初に述べた定義に異なっている理由

instance (CanFilterFunc b a) => CanFilter (b -> b) a where 
    filter = filterFunc 

誰かが説明してもらえますか?おそらく、GHC型推論の実例が役に立つでしょうか?

答えて

49

マイケルはすでにブログ記事でよく説明していますが、私はそれを(考案された、しかし比較的小さい)例で説明しようとします。

私たちは、次の拡張子が必要です:

{-# LANGUAGE FlexibleInstances, TypeFamilies #-} 

ちょうど一つのパラメータで、CanFilterよりも簡単であるクラスを定義してみましょう。

class Twice1 f where 
    twice1 :: f -> f 

class Twice2 f where 
    twice2 :: f -> f 

それでは、各クラスのインスタンスを定義してみましょう:私は2つのインスタンス間の動作の違いを証明したいので、私は、クラスの2つのコピーを定義しています。 Twice1については、型変数を直接同じ変数に修正し、Twice2の場合は、型変数を異ならせることができますが、等価制約を追加します。違いを示すために

instance Twice1 (a -> a) where 
    twice1 f = f . f 

instance (a ~ b) => Twice2 (a -> b) where 
    twice2 f = f . f 

、私たちはこのような別のオーバーロードされた関数を定義してみましょう:

class Example a where 
    transform :: Int -> a 

instance Example Int where 
    transform n = n + 1 

instance Example Char where 
    transform _ = 'x' 

は今、我々は違いを見ることができる点です。我々は

apply1 x = twice1 transform x 
apply2 x = twice2 transform x 

を定義し、推論された型に対してGHCをお願いしたら、我々は

apply1 :: (Example a, Twice1 (Int -> a)) => Int -> a 
apply2 :: Int -> Int 

はなぜそれであることを取得しますか?つまり、Twice1のインスタンスは、関数のソースとターゲットの型が同じ場合にのみ発生します。 transformと指定されたコンテキストについては、わかりません。 GHCは右辺が一度一致するとインスタンスを適用するだけなので、未解決のコンテキストが残っています。 apply1 0と言うと、オーバーロードを解決するのに十分な情報がまだないというエラーがタイプされます。この場合、結果の型をIntに明示的に指定する必要があります。

ただし、Twice2では、インスタンスは任意の関数型用です。 GHCは直ちにそれを解決します(GHCはバックトラックしないので、インスタンスがはっきりとマッチする場合は常に選択されます)。そして、前提条件を確立しようとします。この場合、等式制約は結果タイプをIntに強制し、 Exampleの制約も解決してください。それ以上のタイプの注釈なしでapply2 0と言うことができます。

これは、GHCのインスタンス解決についての微妙な点であり、ここでの等値制約は、GHCのタイプチェッカーが、ユーザーによるより少ないタイプの注釈を必要とするように役立ちます。

+0

ありがとうございました。私はそれのようなものを疑っていましたが、あなたの単純化された例は事をもっとはっきりとさせました。 – Clinton