2017-10-09 7 views
6

でループから相互に再帰的なデフォルトメソッドの防止、私は現在、次のような構造持っている:ランタイム

class F a where 
    f :: ... 
    default f :: (G a...) => ... 
    f = (some definition in terms of g) 

class F a => G a where 
    g :: ... 
    default g :: (C a...) => ... 
    g = (some definition in terms of f) 

うまくいけば、やや平易な英語では、私はいつもgの面でfを書くことができます。 という言葉でgと書くことがあります。私はここを参照してください

問題は、誰かが書き込んだ場合で、用C T

instance F T 
instance G T 

を満たすタイプTこれは、実行時にコンパイルしてループしますと言います。両方のデフォルト定義が正しいのに対し、少なくとも1つは定義されていることが重要です。

fgが同じクラスにあった場合、これはMINIMALプラグマで解決できますが、この場合はそうではありません。

また同じクラスにfgの両方を配置するgのすべての定義のためfの定義がある一方で、fのすべての定義のためgの定義がないよう、可能ではないようです。 1つの可能性はgFに移動することですが、C aという制約も入れていますが、C aを満たしていないaのデフォルト以外の定義でgを定義することができなくなります。

私が直面しているこのジレンマを解決するためにこれを再構成する方法はありますか?

+1

別のフォールバックオプションはG'および/または '' F'のデフォルトの定義を持っているが、その代わりに、インスタンス・ライターがへ*明示的に*オプトインに使用することができ、スタンドアロン機能を提供しないだろう"デフォルト"の実装( 'g = defaultG'を書くことによって)。うまくいけば少なくとも少なくとも1つは意図的な選択だったので、*間違いなく*両方をデフォルトとして残すのが少し難しいことを望みます。 – Ben

答えて

0

私の前の答えはナンセンスだったので、代わりにここに(うまくいけば)良いものです。少なくともコンパイル時には警告が表示されます。トリックは、両方のaFGが、Gのインスタンスが不可能な場合にのみfの両方のインスタンスがある場合は、クラスGf'またはgを実装することです。

{-# LANGUAGE DefaultSignatures #-} 

class C a where 

class F a where 
    f :: a -> a 
    default f :: (G a) => a -> a 
    f = g 

class F a => G a where 
    {-# MINIMAL (f'|g) #-} 

    f' :: a -> a 
    f' = f 

    g :: a -> a 
    default g :: (C a) => a -> a 
    g = f' 

instance F Integer where 
    f = succ 

instance F Int 
instance G Int where 
    g = succ 

instance C Float 
instance F Float 
instance G Float where 
    f' = succ 

-- This will give a compile time warning, but will still 
-- loop at runtime: 
instance C Double 
instance F Double 
instance G Double