2012-04-04 17 views
5

複数のデータ型があり、それぞれが複数の実装を持つことができるHaskellコードを記述しようとしています。これを行うには、それぞれのデータ型をclassとして定義します。そのメソッドは、関連するコンストラクタとセレクタです。次に、指定されたコンストラクタとセレクタに関して、そのクラスのメンバに対してすべての操作を実装します。お楽しみタイプ!複数のインスタンス宣言の解決

例えば、恐らくASparsePoly又はDensePolyBとして表現を有することができる(方法getCoefficientsmakePolynomial有する)多項式クラスがあるとすることができる(方法getRealgetImagmakeComplex有する)複素数クラスでありますComplexCartesianまたはComplexPolarで表されます。

私は以下の最小限の例を再現しました。私は2つのクラスABを持っています。両方のクラスのすべてのインスタンスをNumのインスタンスに自動的に作成したい(これには、FlexibleInstancesUndecidableInstances拡張が必要です)。私はAまたはBのいずれかを持っているとき、これは正常に動作しますが、私は両方でコンパイルしようとすると、私は次のエラーを取得する:

Duplicate instance declarations: 
    instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) => 
         Num (a x) 
    -- Defined at test.hs:13:10-56 
    instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) => 
         Num (b x) 
    -- Defined at test.hs:27:10-56 

私は「重複インスタンス宣言」というメッセージがデータ型のためであると仮定しますABの両方のインスタンスにすることができます。私はそれをしないことを約束したり、型が両方のクラスのインスタンスである場合に使用するデフォルトのクラスを指定したりすることを約束することができるようにしたい。

これを行う方法はありますか(別のタイプの拡張ですか?)、これは私が悩んでいるものですか?

は、ここに私のコードです:

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

class A a where 
    fa :: a x -> x 
    ga :: x -> a x 

data AImpl x = AImpl x deriving (Eq,Show) 

instance A AImpl where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where 
    a1 + a2 = ga (fa a1 + fa a2) 
    -- other implementations go here 


class B b where 
    fb :: b x -> x 
    gb :: x -> b x 

data BImpl x = BImpl x deriving (Eq,Show) 

instance B BImpl where 
    fb (BImpl x) = x 
    gb x = BImpl x 

instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where 
    -- implementations go here 

編集:は、自分自身を明確にするために、私はこの技術を用いて実用的なコードを記述するつもりはありませんよ。タイプシステムとエクステンションの理解を深めるための練習としてやっています。

+4

関連する:[私はどのように書いていますか?もし型付きaなら、この定義ではaもbのインスタンスです。 "](http://stackoverflow.com/a/3216937/98117)。 – hammar

答えて

11

あなたの質問

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

のこの部分を:ベストプラクティスは、あなたがAインスタンスを持っていた後、より多くの機械Numのインスタンスを書くことになり

plusA, minusA :: (A a, Num x) => a x -> a x -> a x 

のようないくつかの定数を定義することです間違っています。あなたはコンパイラが離れて言うことができないという2つのインスタンス、

instance Num (a x) 
instance Num (b x) 

書いてきたので、それが実際です(@のHAMMARさんのコメントからのリンクを参照し、クラスのコンテキストはインスタンス宣言を区別する目的のためにカウントされません)。

1つの解決方法は、証人タイプを追加することです。

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-} 

data AWitness 

data AImpl witness x = AImpl x deriving (Eq,Show) 

instance A (AImpl AWitness) where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where 
    a1 + a2 = ga (fa a1 + fa a2) 

コンパイラは、証人タイプを使用してインスタンス宣言を区別できます。

+0

ありがとう、これは私が行ったアプローチです。 –

4

これを行うには本当に良い方法はありません。

instance A Foo where ... 
instance Num x => Num (Foo x) where 
    (+) = plusA 
    (-) = minusA 
+0

ありがとう!各クラスの実装の数が少ない場合に、これがどのように役立つかがわかります。 –

関連する問題