Suppose I would like to write two Typeclasses. Header:
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Complex
The first typeclass ExClass was defined as such:
class (forall a. (Monoid (t a))) => ExClass t where
exFunc :: [t (Complex a)] -> t (Complex a)
exFunc = mconcat -- the actual function is more complicated
exFunc2 :: (RealFloat a) => t (Complex a) -> a
I defined it as a higher-kinded typeclass because one of its functions' output type depends on the type of its nested value a
. I would also like to have a default implementation for exFunc
, but it involves the assumption that t a
is a Monoid. Now I would like to write an instance for the following type:
newtype ExType a = ExType a
ExType a
is a Monoid only when Num a
is true:
instance (Num a) => Semigroup (ExType a) where
ExType a <> ExType b = ExType (a * b)
instance (Num a) => Monoid (ExType a) where
mempty = ExType 1
Now I go on to define the typeclass instance for ExClass
, specifying the constraint of Num a
:
instance (forall a. Num a) => ExClass ExType where
exFunc2 (ExType a) = magnitude a
The above code will compile with no problem. However, if I were to try to use the implemented function like so:
x = ExType 2 :: ExType (Complex Double)
func = exFunc2 x
I receive the following complaint:
• No instance for (Num a) arising from a use of ‘exFunc2’
Possible fix: add (Num a) to the context of a quantified context
• In the expression: exFunc2 x
In an equation for ‘func’: func = exFunc2 x
This also happens when I use a different instance declaration:
instance (forall a. Monoid(ExType a)) => ExClass ExType where
exFunc2 (ExType a) = magnitude a
Is there a way to make this typeclass work? or am I just not supposed to structure my program like this at all?