2017-11-28 22 views
3

定義される型クラスの問題について考え始め、型クラスを必要とせず、代わりに代数的データ型で私の問題を解決できるようになると、より簡単に思えます。その結果、タイプクラスが必要な時に私は不思議に思っています。タイプクラスと代数データ型

私がそれらを理解しているように、タイプクラスは、ある種の関数があると言う方法です。たとえば、タイプMyTypeがMonoidのインスタンスである場合、関数mempty :: MyTypemappend :: MyType -> MyType -> MyTypeを使用して、モノイドの法則が成立するようにすることができます。

我々はタイプではなく、型クラスとしてモノイドを定義することにより、代数的データ型と同じにacheiveできます

data Monoid a = Monoid { mempty :: a 
         , mappend :: a -> a -> a} 

をした後、型がMyTypeがタイプMonoid MyTypeの新しい値を定義することによってモノイドであると言います(型クラスとそれをインスタンスを宣言することによって行われる):

monoidMyType :: Monoid MyType 
monoidMyType = Monoid { mempty = ... 
         , mappend = \a b -> ... } 

そして、我々のようなモノイドで動作する機能書くことができる:

dummyFun :: Monoid a -> a -> a 
dummyFun m x = mempty m x 

、明示的に適切な「モノイド値」渡すことによって、これらの機能を使用します。同等の最後の2つの手順は型クラスと非常に同様に起こるでしょう

result = dummyFun monoidMyType valueOfMyType 

を:

dummyFun :: (Monoid a) => a -> a 
dummyFun x = mempty x 

result = dummyFun valueOfMyType 

私だけでかなりの違い代数的データ型の場合は、関数dummyFunを呼び出すときにmonoid値を明示的に渡す必要があります。それを明示的に渡す必要がないのはもう少し実用的ですが、大きな障害のように私には見えません。

は実際に、私は代数的データ型は、型クラスの上に持っている利点を参照してください(私は信じている)複数のパラメータを使用して、必要となる型クラスでこれをやって

data Bla a b = Bla {f1 :: a -> b, f2 :: b -> a, ...} 

:あなたはタイプが異なる機能をaccross一緒に関連付けることができますタイプクラス拡張。

ここに表示されないタイプのクラスを使用する理由はありますか?

ソフトウェアを設計するときに、型クラスまたは代数データ型を交換することができますか、型クラスなしでは実行できない状況がありますか?

+0

オタクとADTはかなり直交した概念です。 –

答えて

9

あなたはタイプクラスを作成しました。クラスは、機能の辞書です。コンパイル時に、

class Monoid a where 
    mempty :: a 
    mappend :: a -> a -> a 

instance Monoid [a] where 
    mempty = [] 
    mappend = (++) 

mconcat :: Monoid a => [a] -> a 
mconcat = foldr mappend 

main = print $ mconcat ["foo", "bar"] 

のようなコードは、明示的な辞書渡しスタイルに変換されます。

data Monoid a = Monoid { mempty :: a, mappend :: a -> a -> a } 

list_monoid = Monoid [] (++) 

mconcat :: Monoid a -> [a] -> a 
mconcat monoid = foldr (mappend monoid) 

main = print $ mconcat list_monoid ["foo", "bar"] 

この変換は、型クラスと辞書の間で最も重要な違いです。クラスは暗黙的です。変数monoidを明示的に渡す必要はありません。コンパイラは配管工事を行います。手作業でOrd a => Ord [a]のような構成されたインスタンスを構築するのは特に面倒です。

クラスと辞書の主な違いは、の一貫性です。基本的には、指定された制約を満たすためには、常に最高1つの「最良の」インスタンスが存在します。そのインスタンスはグローバルで一意であり、オーバーライドできません。一方、辞書を渡すスタイルでは、関数は渡された辞書を使用するだけであり、一意性の保証はありません。 This is sometimes good and sometimes bad

+0

「Ord a => Ord [a]のような構成インスタンスを手作業で構築するのは特に面倒です」とはどういう意味ですか?例を挙げていただけますか? –