Off the top of my head i can think of two non-overlapping solution, both are ugly but maybe someone has an idea to make one nice:
The first is to bake the constraint into the GADT:
data MyGADT a where
MyField :: NFData b => b -> MyGADT (Maybe b)
instance NFData (MyGADT a) where
rnf (MyField b) = rnf b
This is bad because NFData should have nothing to do with the definition of the datastructure
The second is to use implication constraints:
import Data.Constraint
data MyGADT a where
MyField :: b -> MyGADT (Maybe b)
newtype Constr a = Constr (forall b. (a ~ Maybe b) :- NFData b)
class DConstr a where
constr :: Constr a
instance (NFData a) => DConstr (Maybe a) where
constr = Constr (Sub Dict)
instance (DConstr a) => NFData (MyGADT a) where
rnf (MyField b) = case constr :: Constr a of
Constr (Sub Dict) -> rnf b
this is ugly because for every non-Maybe index you need to define an "absurd" instance of DConstr. e.g. for Int:
constr = Constr undefined -- should be (Constr (Sub !absurd!)) but there is no way to denote absurdity in Haskell