この疑問はもともと、かなり単純な型クラス メカニズムとしてスタイルを渡す辞書に依存する動的 JS 型バリデーターに関する私の作業中に発生しましたが、型クラス メカニズムを持つ Haskell や他の言語にも当てはまると思います。
最初は、型クラスの解決を完全に制御できるため、ディクショナリを渡すスタイルの設定で、型ごとに複数の型クラス インスタンスを許可してもそれほど大きな問題ではないと考えました。しかし、実際の問題は、型バリデーターを使用してデモンストレーションを行うため、型クラスの解決ではなく、型の安全性を維持しているようです。コードには型注釈が付けられているため、ある程度言語に依存しません。
// please assume `Number` to be a non-polymorphic `Int` type
Sum.Monoid // Monoid<Number>
Prod.Monoid // Monoid<Number>
Function.Monoid // Monoid<b> -> Monoid<(a -> b)>
Function.Monoid(Prod.Monoid) // Monoid<(a -> Number)>
Function.Monoid(Prod.Monoid).append // (a -> Number) -> (a -> Number) -> a -> Number
これらの型を適用すると、型の安全性が損なわれます。これは、バリデーターが文句を言うことなく、次の人工的な式を書くことができるためです。
Function.Monoid(Prod.Monoid)
.append(inc) (inc) (Sum.Monoid.empty); // yields 1 ((0 + 1) * (0 + 1)) instead of 4 ((1 + 1) * (1 + 1))
Haskell では、このような意味のない式を防ぐために、各インスタンスに固有の型があります。しかし、ある型ラッパーから別の型ラッパーに変換しなければならないのは面倒です。
実行可能なタイプセーフな代替手段はありますか、それとも Haskell の設計者がクラスインスタンスごとに個別のタイプの単純化を選択した理由はこれですか?