2016-10-18 11 views
1

コンストラクタのサブタイプを使用して別のタイプの値tを埋め込むタイプSupを構築しました。GHCが欠落している型制約を無視するように強制します。

data Sup t = ... 
      | Sub t 
      deriving Eq 

Supから省略部分がtを使用いずれもコンストラクタの多くを、含まれているので、私はにしたいがEq (Sup t)ではなく、手動でインスタンスを与えることを導き出します。

型制約Eq tSup tため(==)のインスタンス上の場所になりました:

(==) :: Eq t => Sup t -> Sup t -> Bool

次のように述語isSub :: Sup t -> Boolが定義されている:

isSub :: Sup t -> Bool 
isSub (Sub _) = True 
isSub _  = False 

この述語の助けを借りて以下の演算子を定義したいと思います。

supEq :: Sup t -> Sup t -> Bool 
supEq x y = not (isSub x) && not (isSub y) && x == y 

タイプ制約Eq tが存在しないため、上記の定義はGHCでは受け入れられません。しかし、怠惰な評価のおかげで、タイプtの値の間の平等は決して実際には使用されないことがわかります。

GHCに欠落している型制約を無視させる方法はありますか? また、supEqを使用してEq (Sup t)の手動インスタンスを与えることなく、冗長タイプの制約を伝播することなく、supEqという定義を得るために、SupまたはsupEqを定義する方法があります。

+0

この構造にはどのようなユースケースがありますか? – bheklilr

+0

'supEq'は、' supAq'(Sub 1)(Sub 1)≡False'のように、 'Sup a'という形式の2つの値が常に不等であると判断しますか?この問題を明確に述べるのに役立つかもしれません。 – leftaroundabout

+0

私はData.Data.toConstr(https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Data.htmlを参照)を使用して、使用されたコンストラクタの表現を取得してビルドすることができると思いますそこから。 [同様の質問への私の以前の回答](http://stackoverflow.com/questions/2628104/pattern-matching-of-algebraic-type-data-constructors)/ 2630312#2630312 – yatima2975

答えて

4

おそらく最も簡単なことは、カスタムEq (Sup t)インスタンスを定義することです。

instance (Eq t) => Eq (Sup t) where 
    (Sub a) == (Sub b) = a == b 
    A == A = True 
    ... 

あなたは==は(あなたがすべてでsupEqを必要としない)supEqのように動作する場合あるいは、あなたが制約なしでインスタンスを作成することができます。

instance Eq (Sup t) where 
    (Sub a) == (Sub b) = False 
    A == A = True 
    ... 

別のアプローチは、分割することですもちろん

data Sup' = A | B | ... | Z deriving (Eq) -- nothing depends on `t` 
data Sup t = Sub t | Sup' 

supEq :: Sup t -> Sup t -> Bool 
supEq (Sub _) _ = False 
supEq _ (Sub _) = False 
supEq a b = a == b 

まだ最後のオプションは、型システムを破壊することである:2つのデータ型にSup t。これはほとんど間違ったことですが、私はその決定をあなたに任せます。

{-# LANGUAGE ScopedTypeVariables #-} 
import Data.Constraint 

supEq :: forall t . Sup t -> Sup t -> Bool 
supEq x y = let Dict = unsafeCoerce (Dict :: Dict()) :: Dict (Eq t) 
      in not (isSub x) && not (isSub y) && x == y 
+0

あなたの返信をありがとう。 'Dict'ビジネスについて説明できますか? – ltvanbinsbergen

+1

2番目の例の制約のないインスタンスには、反射的ではない '(==)'があり、それは避けられません。 – duplode

+0

最後の例では、 'unsafeCoerce'を使って薄い空気から' Dict(Eq t) 'を作成します。結果( 'Dict')のパターンマッチングによって、' == 'を呼び出すために必要な'(Eq t) 'の(完全に偽の)制約が得られます。あなたが望むようにすべてがうまくいくならば、偽の 'Eq t'制約は決してアクセスされないので、それを取り除くことができます。それは非常に壊れやすく、また、推奨されません。 – crockeea

3

は驚く派生したインスタンスに固執しているときに(==)を使用する場合Eq制約を取り払うの方法はありません。さらに、supEqまでは、インバリアントは強制されていません(間違いを犯してTrueFalseisSubに入れ替えた場合どうなるかを考慮してください)。おそらくSupパターンの面でsupEqを書くことだけでは一致したほうが良いでしょう:

data Sup t = Foo 
      | Bar 
      | Sub t 
      deriving Eq 

supEq :: Sup t -> Sup t -> Bool 
supEq (Sub _) _ = False 
supEq _ (Sub _) = False 
supEq Foo Foo = True 
supEq Bar Bar = True 
supEq _ _ = False 

このようにsupEqを書くことが迷惑になるという十分な例がある場合は、あなたが別の型に非Sub例を分割することができます、とcrockeeaの答えの次のツー最後の例では、完全を期すために以下に再現:

data Sup' = Foo | Bar deriving (Eq) 
data Sup t = Sub t | NotSub Sup' deriving (Eq) 

supEq :: Sup t -> Sup t -> Bool 
supEq (Sub _) _ = False 
supEq _ (Sub _) = False 
supEq (NotSub a) (NotSub b) = a == b 
3

はもちろん、最も簡単な解決策は、他の人が提案してきたように、二つのタイプにあなたのタイプを分割することです。しかし、これはシンタックスノイズを生成します - コンストラクタの余分なレベル。あなたは、両方の最高をしたい場合は、reflectionを使用することができます。

import Data.Reflection 
import Data.Proxy 
import Data.Coerce 

data Sup t = Sub t | A | B | C | D | E -- .. etc 
    deriving (Eq, Show) 

SubSubを比較するときに我々は今、Sup用に生成Eqコードを使用するが、異なる機能で代替ではなく、(==)ます。

まず、あなたは(私はこれがreflectionパッケージ自体にすべきだと思います - それはMonoidApplicativeためsimilairコードを持っている)、いくつかの設定が必要です

newtype ReifiedEq a = ReifiedEq { eq :: a -> a -> Bool } 
newtype ReflectedEq s a = ReflectedEq a 

instance Reifies s (ReifiedEq a) => Eq (ReflectedEq s a) where 
    (==) = coerce (eq (reflect (Proxy :: Proxy s))) 

これは、ソリューションの心臓部である - 型の値ReflectedEq s aはちょうどaですが、同等かどうかを比較すると、いつでも指定できるReifiesの等価関数が使用されます。 reflectionパッケージは、複数のEqインスタンスが同じコンテキストで使用されないように、タイプレベルの機械を使用しています。

今、あなたは、より一般的なご希望の1以上の機能を書き込むことができます。

supEqWith :: (t -> t -> Bool) -> Sup t -> Sup t -> Bool 
supEqWith k x y = reify (ReifiedEq k) (\p -> h p x == h p y) where 
    h :: Proxy s -> Sup a -> Sup (ReflectedEq s a) 
    h _ = coerce 

をこの関数は、単にSubt値を比較するために指定された関数(k)を使用して、平等のためのSup値を比較します。ファントム型パラメータ(s)を正しく指定するには、h関数が必要です。それ以外の場合はあいまいです。

あなたの所望の機能は単純です:

supEq = supEqWith (\_ _ -> False) 
3

あなたSupタイプはtでファンクタ(deriving Functorはあなたが与えてくれた例で動作します)することができ、あなたは他のコンストラクタのいずれも使用しないことがわかっている場合t、あなたはfmap (const())です。

tは、等価チェックに興味深い影響を及ぼさず、オリジナルのtEqである必要はありません。両方の入力がSub _だった場合は、真実ではなく偽を返すようにする必要があります。あなたがSupファンクタを作りたくない場合でも

subEq (Sub _) _ = False 
subEq _ (Sub _) = False 
subEq x y = fmap (const()) x == fmap (const()) y 

、あなたはまだ submap :: (a -> b) ->Sup a -> Sup bを実装して、それを使用することができます。

関連する問題