より優れた柔軟性と構成可能性が必要な場合は、型クラスをレコードとして具体化します。RankNTypes
必要な場合があります。
たとえば、Applicative 型クラスを具体化する方法は次のとおりです。
{-# LANGUAGE RankNTypes #-}
data ApplicativeInstance f = ApplicativeInstance
{ pure :: forall a. a -> f a
, amap :: forall a b. (a -> b) -> f a -> f b
, ap :: forall a b. f (a -> b) -> f a -> f b
}
listApplicative = ApplicativeInstance
{ pure = \a -> [a]
, amap = map
, ap = \fs xs -> case fs of
[] -> []
f:fs' -> map f xs ++ ap cartesianListApplicative fs' xs
}
zipListApplicative = ApplicativeInstance
{ pure = \a -> [a]
, amap = map
, ap = \fs xs -> case (fs, xs) of
([], _) -> []
(_, []) -> []
(f:fs', x:xs') -> f x : ap zipListApplicative fs' xs'
}
これで、必要なインスタンスを指定できるようになりました。ただし、インスタンスを暗黙的に選択する力は失われます。選択は明示的に行う必要があります。
ghci> ap listApplicative [(+1), (*3)] [1 .. 5]
[2,3,4,5,6,3,6,9,12,15]
ghci> ap zip
zip zipListApplicative zipWith3
zip3 zipWith
ghci> ap zipListApplicative [(+1), (*3)] [1 .. 5]
[2,6]
参照:
http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/