2016-08-30 17 views
2

私は現在、クラスのいくつかのインスタンスを派生させるプロジェクトで作業しています。クラスにはいくつかのケースで同じ定義を保存するメソッドが1つしかないので、オーバーラップ可能な一般的なインスタンスを定義し、重複する必要があるものを定義してみました。重複するインスタンスの問題

重複するインスタンスエラーが発生するため、これは機能しません。いくつかのテストをして、私たちは、私の元の問題にかなり多くの同等です。この減少した例accrossに来た:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses #-} 

module Instance where 

data Id a = Id a String 

data C a = C a 

class Bad a b where 
    bad :: a -> String 

instance {-# OVERLAPPABLE #-} Bad a b where 
    bad = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Bad (Id a) (C a) where 
    bad = \_ -> "Id" 

class Good a b where 
    good :: a -> String 

instance {-# OVERLAPPABLE #-} Good a b where 
    good = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Good (Id a) b where 
    good = \_ -> "Id" 

test = let a = Id() "a" 
     in putStrLn (good a) >> putStrLn (bad a) 

(あなたが二悪いのインスタンスをコメントしていない限り、これはコンパイルされませんので注意してください。)

クラス良い作品(テスト出力「Id」)。私が悪いため2番目のインスタンスを削除しない場合は、私が手:

Overlapping instances for Bad (Id()) b0 
    arising from a use of ‘bad’ 
    Matching instances: 
    instance [overlappable] Bad a b -- Defined at Instance.hs:12:31 
    instance [overlapping] Bad (Id a) (C a) 
     -- Defined at Instance.hs:15:30 
    (The choice depends on the instantiation of ‘b0’ 
    To pick the first instance above, use IncoherentInstances 
    when compiling the other instance declarations) 
    In the first argument of ‘putStrLn’, namely ‘(bad a)’ 
    In the second argument of ‘(>>)’, namely ‘putStrLn (bad a)’ 
    In the expression: putStrLn (good a) >> putStrLn (bad a) 

私は理解していないことは、それらの間の唯一の違いは、2番目のクラスのパラメータでaditional制限されたときに、なぜこれが起こるんです。

重複エラーを避けるために、重ねて表示できるインスタンスのポイントではありませんか?

よろしく

+0

これらのクラスは実際には機能しません。クラス内の関数は変数 'b'に言及せず、' a'から 'b'を決定するためのfundepなどはありません。 「良い」は顕著な偶然のためにのみ作用します.-両方とも「良い」の場合と、2番目のタイプが完全に制限のないタイプの変数の場合です。これは、クラスに型変数がまったくない場合と本質的に同じです。 'b'は後で' C a'にインスタンス化され、どのインスタンスが選択されているかを変更する( "選択はインスタンス化に依存する")完全に自由な型の変数であるため、 '悪い 'は機能しません。 – user2407038

+0

あなたのプラグマは 'UndecidableInstances'の代わりに' AllowAmbiguousTypes'を持つべきだと思います。そうでなければ、 'bad :: Bad ab => a - > String'の' b'にあいまいなエラーを返します。 – Alec

+0

@ user2407038はい、私はそれを見ることができますが、関数にbを追加するとエラーは残っています。もう少しテストすると、基本的に関数を使用しなければならないことがわかりましたが、明示的にbが何であるかをghcはどのインスタンスを使用するのか知っています。私はこの小さな例をうまく利用することができましたが、これは大きなプロジェクトでは実行可能ではないと私は考えています。ありがとう! –

答えて

3

上記の私のコメントごとに、私はあなたのプラグマではなくUndecidableInstancesAllowAmbiguousTypesを持つべきだと思うと、そうでない あなたは別のエラーが発生します(少なくとも私はGHC 8.0.1上で行うには)で曖昧であることbに関するファンクションシグネチャ bad :: Bad a b => a -> String

AmbiguousTypesを使用すると、使用時に曖昧な機能の署名を書くことができます。 代わりに、あいまいチェックがコールサイトに移動されます。これはあいまいな変数を指定するのにTypeApplications のようなものでうまく動作します。この場合、badは常に曖昧なので、このプラグマをコールサイトの エラーメッセージに移動する必要があります。今、私はあなたと同じメッセージを持っています。


でもとOVERLAPPABLEOVERLAPPING Haskellは文句の理由は、( が指定されていない)bがインスタンス化された方法に応じて、それはBadの2つのインスタンスのいずれかを選択することです。 Haskellはその手をスローし、私が選ぶこと できるようにするために、「あなたは十分な程度b私に言っていないと言うので、言い換えれば、あなたは、bはちょうど同様にあなたができなかったとしてC a、 で統一するしたいことBadの最も具体的なインスタンスです。一方

は、偶数bを知ることなく、Haskellはより 特異的でインスタンスGood a bGood (Id a) bのかを知っている - それは常に(さえbそのような場合は何であるか知らず)は、第2のものです。

overlapping instances のドキュメントを読むことをお勧めします。アルゴリズム全体が説明されています。


あなたは通常(bを指定する)TypeApplicationsのようなものを使用して、またはタイプファミリーにあなたのタイプのクラスを変換するこれらの問題を回避することができます。

関連する問題