I've found one way to generalize the idea of joining multiple classes into one. Although it's quite awkward to use, it works.
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Test where
import GHC.Exts
data (:>+<:) ctx1 ctx2 a where
C'C :: (ctx1 a, ctx2 a) => (ctx1 :>+<: ctx2) a
data (:++:) (ctx1 :: * -> Constraint) (ctx2 :: * -> Constraint) = C
class Ctx2 c1 c2 a where
useCtx :: c1 :++: c2 -> a -> ((c1 :>+<: c2) a -> b) -> b
instance (c1 a, c2 a) => Ctx2 c1 c2 a where
useCtx _ a f = f C'C
fC :: (Ctx2 Num Enum a) => a -> a
fC a = useCtx (C :: Num :++: Enum) a $ \C'C -> 3 + succ a
data A ctx = A
{ call :: forall a. ctx a => a -> a
}
main = print $ call (A fC :: A (Ctx2 Num Enum)) 3
It should be possible to use constraint aliases to define Ctx3, Ctx4, ...