2016-03-23 17 views
3

私は一緒に次のクラスDomainとそのインスタンスTrivialDomainタイプ曖昧

{-# LANGUAGE TypeFamilies #-} 

data Transition = Transition 

class Domain d where 
    type Set d 
    type Engine d :: * -> * 

    top :: Engine d (Set d) 

    -- ... 
    complement :: Set d -> Engine d (Set d) 
    exclude :: Set d -> Set d -> Engine d (Set d) 
    -- ... 

data TrivialDomain = TrivialDomain 

instance Domain TrivialDomain where 
    type Set TrivialDomain = [Int] 
    type Engine TrivialDomain = IO 

    top = return [0..10] 

    -- ... 
    complement a = top >>= (flip exclude) a 
    exclude a b = return $ filter (not . (`elem` b)) a 
    -- ... 

を入れしようとしていますが、私は、私は私が

test3.hs:25:21: 
    Couldn't match type ‘Engine d0’ with ‘IO’ 
    The type variable ‘d0’ is ambiguous 
    Expected type: IO (Set d0) 
     Actual type: Engine d0 (Set d0) 
    In the first argument of ‘(>>=)’, namely ‘top’ 
    In the expression: top >>= (flip exclude) a 
test3.hs:25:35: 
    Couldn't match type ‘Set d1’ with ‘[Int]’ 
    The type variable ‘d1’ is ambiguous 
    Expected type: Set d0 -> [Int] -> IO [Int] 
     Actual type: Set d1 -> Set d1 -> Engine d1 (Set d1) 
    In the first argument of ‘flip’, namely ‘exclude’ 
    In the second argument of ‘(>>=)’, namely ‘(flip exclude) a’ 

を理解するために失敗し、次のエラーが発生します取得しておきますインスタンスの宣言でEngine d (Set d)IO [Int]に解決されると予想されますが、そうではありません。少なくともGHCはそうは思わない。私は何が欠けていますか?

答えて

6

あなたのケースでは、関連付けられたタイプではメソッドのタイプを推測するには不十分です。

あなたはクラスDomain dを持ち、SetEnginedに関連付けられています。これは、知られているDomain dインスタンスを持つ私たちのプログラムに既知のdがあるときはいつでも、GHCはSet dEngine dを解決できます。しかしこれは後方には働かない。 GHCはdインスタンスまたはDomainインスタンスをSet dまたはEngine dの存在から解決できません。異なるDomainインスタンスがあり、同じSetおよびEngineタイプを持つ可能性があります。

クラスメソッドはSetEngineとしか記述していないので、メソッドの使用から決して推測することはできません。

あなたの目標に応じていくつかのことができます。

まず、あなたはdSetEngineに依存して作ることができます:

class Domain set engine where 
    type DomainOf set engine :: * 
    -- ... 

は、より一般的に、FunctionalDependenciesはあなたのタイプ間の依存関係を強制するより多くの柔軟性を提供します。たとえば、あなたが特に一つだけd良い型推論回復するのに十分である各Set、用があることを宣言することができます。あなたはあなたの元のクラスを使用する場合は、最後に

class Domain d set engine | d -> set engine, set -> d where 

    top  :: engine set 
    complement :: set -> engine set 
    exclude :: set -> set -> engine set 

data TrivialDomain = TrivialDomain 

instance Domain TrivialDomain [Int] IO where 

    top = return [0..10] 

    complement a = top >>= (flip exclude) a 

    exclude a b = return $ filter (not . (`elem` b)) a 

を、あなたはProxy dパラメータを追加する必要がありますあなたの方法に、インスタンスと関連付けられているタイプに解決をするために:ここ

import Data.Proxy 

data Transition = Transition 

class Domain d where 
    type Set d 
    type Engine d :: * -> * 

    top  :: Proxy d -> Engine d (Set d) 
    complement :: Proxy d -> Set d -> Engine d (Set d) 
    exclude :: Proxy d -> Set d -> Set d -> Engine d (Set d) 

data TrivialDomain = TrivialDomain 

instance Domain TrivialDomain where 
    type Set TrivialDomain = [Int] 
    type Engine TrivialDomain = IO 

    top _ = return [0..10] 

    complement d a = top d >>= (flip (exclude d)) a 
    exclude d a b = return $ filter (not . (`elem` b)) a 

Proxy dの目的は、あなたが使用したいインスタンスを正確に指定することです。

しかし、これは、それぞれのメソッドの使用方法(他の方法と同様)でtop (Proxy :: Proxy d)と書く必要があることを意味します。むしろ面倒です。 GHC 8で、私たちはProxy秒を省略し、代わりにTypeApplicationsを使用することができます。最後の例について

{-# language TypeApplications, TypeFamilies #-} 

-- ... 

instance Domain TrivialDomain where 
    type Set TrivialDomain = [Int] 
    type Engine TrivialDomain = IO 

    top = return [0..10] 

    complement a = top @TrivialDomain >>= (flip (exclude @TrivialDomain)) a 
    exclude a b = return $ filter (not . (`elem` b)) a 
+0

を、あなたは 'GHC8でクラスレベルでD '@トップを書くことができますか? – jakubdaniel

+0

私たちはできないと思います。デフォルトでは、 'forall'バインド型の変数は' @ 'で適用できますが、 'Domain'メソッドの型に' forall d。'を書くことはできません。私は '@'がクラスメソッドでうまく動作し、クラス型のパラメータの順番で使用できることを発見しました。 –