これは、クラス定義で汎用メソッドを定義し、インスタンスでオーバーライドすることにより、型クラスで実行できます。オーバーライドされた関数が常に使用されます。
class ContainsInt c where
toList :: c -> [Int]
-- generic function
elem :: Int -> c -> Bool
elem n x = Prelude.elem n (toList x)
instance ContainsInt () where
toList _ = []
-- Override the generic function for type ()
elem _ _ = False
GHC でサポートされている代替手段は、書き換え規則を使用することです。書き換え規則は GHC に、可能な限りある式を別の式に置き換えるように指示します。置換の型が間違っている場合は置換されないため、これを使用して関数を特殊なバージョンに置換できます。書き換え規則は{-# RULES #-}
プラグマで与えられます。
class ContainsInt c where
toList :: c -> [Int]
elem :: ContainsInt c => Int -> c -> Bool
elem n x = Prelude.elem n (toList x)
-- Replace 'elem' by 'elemUnit' if it has the same type
{-# RULES "elem()" forall. elem = elemUnit #-}
elemUnit :: Int -> () -> Bool
elemUnit _ _ = False
書き換え規則はコンパイラの裁量で実行されるため、特定の状況で特殊な関数が呼び出される場合と呼び出されない場合があります。たとえば、書き換えは、コンパイラが関数をインライン化するかどうかによって異なります。
foo :: ContainsInt c -> Int -> [c] -> [Bool]
-- Must use the generic function
foo n cs = map (elem n) cs
useFoo :: Int -> [()] -> [Bool]
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'.
-- Otherwise rewriting cannot happen.
useFoo n cs = foo n cs