5

私は、それぞれが複数の実装を持つことができる複数のデータ型があるHaskellコードを書き込もうとしています。これを行うには、各データ型を、classそのメソッドが関連するコンストラクターとセレクターであるとして定義し、次に、指定されたコンストラクターとセレクターに関して、そのクラスのメンバーに対するすべての操作を実装します。

たとえば、おそらくは、aまたはaとして表現できるA多項式クラス(メソッドgetCoefficientsおよびmakePolynomial)であり、aまたはaとして表現できる複素数クラスSparsePoly(メソッドおよび)です。DensePolyBgetRealgetImagmakeComplexComplexCartesianComplexPolar

以下に最小限の例を再現しました。私には2つのクラスがAあり、Bそれぞれに実装があります。両方のクラスのすべてのインスタンスをNum自動的にのインスタンスにしたい(これにはFlexibleInstancesUndecidableInstancesタイプの拡張子が必要です)。Aこれは、またはの1つしかない場合は正常に機能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

'重複インスタンス宣言'メッセージは、データ型がとの両方のインスタンスになる可能性があるためだと思いAますB。コンパイラーにそうしないことを約束できるようにしたい、または型が両方のクラスのインスタンスである場合に使用するデフォルトのクラスを指定できるようにしたい。

これを行う方法はありますか(おそらく別のタイプの拡張機能ですか?)、またはこれは私が立ち往生しているものですか?

これが私のコードです:

{-# 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

2 に答える 2

12

あなたの質問のこの部分

'duplicate instancedeclarations'メッセージは、データ型をAとBの両方のインスタンスにすることができるためだと思います。コンパイラーにそれを行わないことを約束できるようにしたい、またはおそらくタイプが両方のクラスのインスタンスである場合に使用するデフォルトのクラス。

間違っている。実際には、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)

コンパイラーは、ウィットネス・タイプを使用して、インスタンス宣言を区別できます。

于 2012-04-04T17:24:38.220 に答える
5

これを行うための本当に良い方法はありません。ベスト プラクティスは、次のような定数を定義することです。

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

これにより、インスタンスを作成したNum後、インスタンスをより機械的に記述できAます。

instance A Foo where ...
instance Num x => Num (Foo x) where
    (+) = plusA
    (-) = minusA
于 2012-04-04T16:50:17.540 に答える