2011-12-16 21 views
11

動作しない私は、これは、例えば、正常に動作するようです一見無害型クラスのインスタンスは、型クラスで遊ん

class Pair p a | p -> a where 
    one :: p -> a 
    two :: p -> a 

を思い付い

instance Pair [a] a where 
    one [x,_] = x 
    two [_,y] = y 

しかし、私はタプルに問題があります。以下の定義はコンパイルにもかかわらず、私は予想通り...

instance Pair (a,a) a where 
    one p = fst p 
    two p = snd p 

...私はそれを使用することはできません。正しくインスタンスを定義する方法が

main = print $ two (3, 4) 

No instance for (Pair (t, t1) a) 
    arising from a use of `two' at src\Main.hs:593:15-23 
Possible fix: add an instance declaration for (Pair (t, t1) a) 
In the second argument of `($)', namely `two (3, 4)' 
In the expression: print $ two (3, 4) 
In the definition of `main': main = print $ two (3, 4) 

ありますか?または、newtypeラッパーに頼らざるを得ませんか?

答えて

18

インスタンスは実際にはうまく動作します。観察:

main = print $ two (3 :: Int, 4 :: Int) 

これは期待どおりに動作します。それでは、型アノテーションなしではなぜ動作しないのですか?さて、タプルのタイプを考えてみましょう:(3, 4) :: (Num t, Num t1) => (t, t1)。数値リテラルは多型であるため、には同じ型のが必要です。インスタンスは(a, a)のために定義されていますが、そのインスタンスの存在はGHCに型の統一を指示しません(さまざまな理由があります)。 GHCが2つのタイプが同じであることを他の方法で推論することができない限り、に等しいことができる場合でも、あなたは必要なインスタンスを選択しません。

上記のように、問題を解決するには、タイプ注釈を追加するだけです。引数が他の場所から来ている場合、それらはすでに同じ型であることが知られているので通常は不要ですが、数値リテラルを使用する場合はすぐに不調です。

インスタンス選択の仕組みのために、(a, a)のインスタンスを持つと、(a, b)のようなインスタンスも作成できないことに注意してください。だから我々はこのように、型クラスを使用して統一を強制するために、ビットをごまかすことができます:~コンテキストのTypeFamilies延長を必要とする

instance (a ~ b) => Pair (a,b) a where 

を、私は思います。これは、インスタンスの選択がコンテキストを無視するため、最初に任意のタプルでインスタンスを一致させることができます。しかし、インスタンスを選択した後では、a ~ bコンテキストは型の等価性をアサートします。異なる場合はエラーを生成しますが、可能であれば型変数を統一します。これを使用して、mainの定義は、注釈なしでそのまま動作します。

+0

ありがとう、非常に面白い! – Landei

6

問題は、リテラル番号が多型であることです。 2つのリテラルが同じタイプ(Int)である必要があることは、型チェッカーには明らかではありません。あなたのタプルに多型でないものを使用すると、コードが機能するはずです。次の例を参考にしてください。

*Main> two (3,4) 

<interactive>:1:1: 
    No instance for (Pair (t0, t1) a0) 
     arising from a use of `two' 
    Possible fix: add an instance declaration for (Pair (t0, t1) a0) 
    In the expression: two (3, 4) 
    In an equation for `it': it = two (3, 4) 
*Main> let f = id :: Int -> Int -- Force a monomorphic type 
*Main> two (f 3,f 4) 
4 
*Main> two ('a','b') 
'b' 
*Main> two ("foo","bar") 
"bar" 
*Main> two (('a':),('b':)) "cde" 
"bcde" 
*Main> 
関連する問題