Reputation: 10366
I have something like the following:
{-# LANGUAGE TypeFamilies #-}
class Configuration c where
data Pig c
data Cow c
parsePig :: GenParser Char st (Pig c)
parseCow :: GenParser Char st (Cow c)
data Farm c =
{ pigs :: [Pig c]
, cows :: [Cow c]
} deriving Show
This fails because of the deriving Show
line. I don't know how to force all Configuration
instances to ensure that their data Pig
and data Cow
implementations are all instances of Show
.
I know I could make it have showPig
and showCow
methods and the write out the whole complex show
instance, but in reality things are more complex than this and that would be quite a pain.
Is there an easy, elegant way to guarantee that type family instances are themselves instances of certain classes?
Upvotes: 4
Views: 150
Reputation: 3246
Since you said you wanted to force all instances of Configuration
to have Pig c
and Cow c
implement Show
, an even simpler way to do this is to simply constrain the type families in the class's context, like so:
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
class (Show (Pig c), Show (Cow c)) => Configuration c where
data Pig c
data Cow c
data Farm c = Farm { pigs :: [Pig c],
cows :: [Cow c] } deriving (Show)
EDIT:
As @hammar pointed out in his comment, the previous code won't compile. One way to fix this is to use StandaloneDeriving
, as he suggested. The other way is this:
{-# LANGUAGE TypeFamilies, FlexibleContexts, GADTSyntax #-}
class (Show (Pig c), Show (Cow c)) => Configuration c where
data Pig c
data Cow c
data Farm c where
Farm :: Configuration c => { pigs :: [Pig c],
cows :: [Cow c] } -> Farm c deriving (Show)
These two approaches get you slightly different results, in that @hammar's approach will require a Configuration
constraint if you call show
, whereas my approach will make available said constraint.
Upvotes: 1
Reputation: 139840
You can use StandaloneDeriving
to specify the constraints manually just for the Show
instance.
{-# LANGUAGE StandaloneDeriving, FlexibleContexts, UndecidableInstances #-}
deriving instance (Show (Pig c), Show (Cow c)) => Show (Farm c)
This will still let you have Configuration
instances with Cow
and Pig
that don't implement Show
, however, as long as you don't try to show
them.
Upvotes: 7