13

実際の Haskell には次の例があります。

class BasicEq3 a where
    isEqual3 :: a -> a -> Bool
    isEqual3 x y = not (isNotEqual3 x y)

    isNotEqual3 :: a -> a -> Bool
    isNotEqual3 x y = not (isEqual3 x y) 

instance BasicEq3 Bool

そして、GHCIで実行すると:

#> isEqual3 False False
out of memory

したがって、2 つのメソッドの少なくとも 1 つを実装する必要があります。そうしないと、ループします。そして、あなたはどれがきちんとしたものかを柔軟に選択できます。

私が持っている質問は、十分なデフォルトをオーバーライドせず、デフォルトがループを形成する場合、警告または何かを取得する方法はありますか? 非常にスマートなコンパイラがこの例で問題ないことは、私には奇妙に思えます。

4

5 に答える 5

12

いいえ、残念ながら GHC はそのようなことはしません。また、一般的には不可能です。

おわかりのように、型クラスのメソッドは、便利な方法で相互に再帰的になる可能性があります。これは、そのような型クラスの不自然な例です。デフォルトの実装が相互に関係している場合でも、 または のsumOddsいずれかを定義しなくてもまったく問題ありません。sumEvens

class Weird a where
    measure :: a -> Int

    sumOdds :: [a] -> Int
    sumOdds [] = 0
    sumOdds (_:xs) = sumEvens xs

    sumEvens :: [a] -> Int
    sumEvens [] = 0
    sumEvens (x:xs) = measure x + sumOdds xs
于 2012-09-04T19:49:57.027 に答える
12

「壊れていない」循環依存の場合に GHC が警告を出すのは全く問題ないと思います。これらの行に沿ったチケットもあります: http://hackage.haskell.org/trac/ghc/ticket/6028

何かが「決定不能」だからといって、問題のインスタンスが効果的に解決できないという意味ではありません。GHC (または他の Haskell コンパイラ) には、必要な情報がすでにかなりあります。ユーザーが循環依存関係を「壊す」ことなくクラスをインスタンス化している場合、GHC が警告を発することは完全に可能です。また、以前の投稿で例示されているように、まれにコンパイラが間違った場合、ユーザーは-nowarnundefinedcyclicmethodsGHC に静かにするように伝えるための、または同様のメカニズムを持つことができます。他のほとんどの場合、警告は大歓迎であり、プログラマーの生産性を向上させます。ほとんどの場合ばかげたバグを回避します。

于 2012-09-04T21:15:46.983 に答える
6

いいえ、ありません。コンパイラがこの決定を行うことができれば、それは停止問題を解決することと同じになるからです。一般に、2 つの関数が「ループ」パターンで相互に呼び出しているという事実だけでは、関数の 1 つを実際に呼び出すとループが発生すると結論付けるには不十分です。

(考案された)例を使用するには、

collatzOdd :: Int -> Int
collatzOdd 1 = 1
collatzOdd n = let n' = 3*n+1 in if n' `mod` 2 == 0 then collatzEven n'
                                   else collatzOdd n'

collatzEven :: Int -> Int
collatzEven n = let n' = n `div` 2 in if n' `mod` 2 == 0 then collatzEven n'
                                        else collatzOdd n'

collatz :: Int -> Int
collatz n = if n `mod` 2 == 0 then collatzEven n else collatzOdd n

(もちろん、これはコラッツ予想を実装する最も自然な方法ではありませんが、相互に再帰的な関数を示しています。)

ここcollatzEvenで、 とcollatzOddは互いに依存していますが、コラッツ予想ではcollatz、すべての正の に対して呼び出しが終了すると述べていnます。collatzOddGHC がデフォルト定義としてandを持つクラスがcollatzEven完全な定義を持つかどうかを判断できれば、GHC はコラッツ予想を解くことができます! (もちろん、これは停止問題の決定不能性の証明ではありませんが、相互に再帰的な一連の関数が明確に定義されているかどうかを判断することが、見かけほど簡単ではない理由を示しているはずです。)

一般に、GHC はこれを自動的に決定できないため、Haskell クラスのドキュメントでは、クラスのインスタンスを作成するために必要な「最小限の完全な定義」を提供します。

于 2012-09-04T19:59:14.273 に答える
2

私の個人的な意見では、デフォルトのメカニズムは不要であり、賢明ではありません。クラスの作成者がデフォルトを通常の関数として提供するのは簡単です。

notEq3FromEq3 :: (a -> a -> Bool) -> (a -> a -> Bool)
notEq3FromEq3 eq3 = (\x y -> not (eq3 x y))

eq3FromNotEq3 :: (a -> a -> Bool) -> (a -> a -> Bool)
eq3FromNotEq3 ne3 = (\x y -> not (ne3 x y))

(実際、これら2つの定義は同じですが、一般的には当てはまりません)。次に、インスタンスは次のようになります。

 instance BasicEq3 Bool where
   isEqual3 True True = True
   isEqual3 False False = True
   isEqual3 _ _ = False

   isNotEqual3 = notEq3FromEq3 isEqual3

デフォルトは必要ありません。次に、定義を指定しない場合、GHCは警告を表示できます。また、不快なループは、コードに明示的に記述する必要があります。

これにより、既存のインスタンスに影響を与えることなく、デフォルトの定義を持つクラスに新しいメソッドを追加する優れた機能が削除されますが、私の見解ではそれほど大きなメリットはありません。上記のアプローチは、原則としてより柔軟です。たとえば、インスタンスが実装する比較演算子をOrd選択できるようにする関数を提供できます。

于 2012-09-05T13:42:34.640 に答える
2

私はそうは思わない。コンパイラが停止の問題を解決することを期待しているのではないかと心配しています! 2 つの関数が相互に定義されているからといって、それがデフォルト クラスとして不適切であるとは限りません。instance MyClass MyTypeまた、便利な機能を追加するために書く必要があるクラスを過去に使用しました。したがって、コンパイラにそのクラスについて警告するように求めることは、他の有効なコードについて文句を言うように要求することです。

[もちろん、開発中に ghci を使用し、作成後にすべての機能をテストしてください! HUnitおよび/またはQuickCheckを使用して、このようなものが最終的なコードにならないようにしてください。]

于 2012-09-04T19:56:22.870 に答える