下位互換性以外にも、Haskell が型クラスを扱うかなり単純な方法に起因する (ほとんどは表面的なものですが、対処するのが面倒な) 問題がいくつかあります。
上向きの暗黙的な定義はありません:はandMonad
だけで完全に定義されます。そしてそれらの言葉で書くことができます。「適切な」階層では、各インスタンスを書き出す必要があります。この場合はそれほど悪くはありませんが、粒度が上がるにつれて、それぞれがいくつかの小さな機能を追加するインスタンスの数も増えます。クラス定義が独自の関数に関してスーパークラス関数のデフォルトの実装を提供できれば、物事は大幅に簡素化されます。そのため、スーパークラス内の複数の関数を完全に包含するような関数は、関連する階層全体の定義として機能できます。pure
(>>=)
fmap
(<*>)
(>>=)
Context bloatNum
: and を必要Show
とする「理由」がある限りEq
、それは数値の等価性を印刷して比較することがかなり一般的であるためです。これらは厳密に直交していますが、それらを分離すると、3 つすべてを実行する関数は、その型で 3 つのクラスすべてを指定する必要があります。これは技術的には良いことですが、繰り返しになりますが、粒度が上がると関数型のシグネチャも大きくなります。
モノリシックな依存関係: 型クラスとそのスーパークラスの階層を追加することはできますが、変更または置換することはできません。コードの一部が、共通の型クラスの独自のバージョンを置き換える必要があると感じた場合 (たとえば、このようなものを使用して置き換えるMonad
場合)、階層はその時点で切断されます。の他の定義を使用するコードとの互換性は、Monad
ある程度手動で提供する必要があり、他の定義の上に構築された型クラスは、両方の定義が共有する動作のサブセットのみに依存している場合でも、再実装または変換する必要があります。
明確に正しい階層はありません: 上記で暗示されているように、クラスの粒度には選択が必要です。たとえば、Pointed
本当に存在する必要があるのでしょうか、それともApplicative
十分でしょうか? ここには、普遍的に理想的な答えはありませんし、存在する必要もありません。
既存の型クラスを置き換える作業は、最初に上記の問題に取り組むことでよりうまく機能するのではないかと思います。その後、型クラスを置き換えることは、はるかに苦痛が少なくなるか、形式的なものにすぎません。たとえば、@luqui が言及している型クラス シノニムの提案は、この方向への大きな一歩となるでしょう。