これは、GHC 7.4のジェネリックプログラミングサポートを使用して、比較的クリーンかつ効率的に行うことができます。GHC.Genericsのドキュメントが役立つ場合があります。これが例です。
次のサンプルクラスといくつかのサンプルインスタンスについて考えてみます。
class C a where
-- | Double all numbers
double :: a -> a
instance C Int where
double i = 2 * i
instance (C a, C b) => C (a, b) where
double (a, b) = (double a, double b)
いくつかの言語プラグマとインポートが必要です。
{-# LANGUAGE TypeOperators, DefaultSignatures, DeriveGeneric, FlexibleContexts, FlexibleInstances #-}
module Example where
import GHC.Generics hiding(C, D)
ここで、いくつかの「一般的なインスタンス」を示します。ジェネリック型にはすべてファントムパラメータx
があり、インスタンスヘッドが少し複雑になります。
-- "Insert" a normal value into a generic value
instance C c => C (K1 i c x) where
double (K1 c) = K1 (double c)
-- Ignore meta-information (constructor names, type names, field names)
instance C (f x) => C (M1 i c f x) where
double (M1 f) = M1 (double f)
-- Tuple-like instance
instance (C (f x), C (g x)) => C ((f :*: g) x) where
double (f :*: g) = double f :*: double g
クラスC
を再定義して、GC
class C a where
-- | Double all numbers
double :: a -> a
-- specify the default implementation for double
default double :: (Generic a, C (Rep a ())) => a -> a
double = to0 . double . from0
-- from, with a more specialised type, to avoid ambiguity
from0 :: Generic a => a -> Rep a ()
from0 = from
-- to, with a more specialised type, to avoid ambiguity
to0 :: Generic a => Rep a () -> a
to0 = to
これで、いくつかのインスタンスを非常に簡単に定義できます。
data D a = D a a a deriving Generic
instance C a => C (D a)
data D2 m = D2 (m Int) (m Int) deriving Generic
instance C (D2 D)