I'm not sure if you need a universal or existential quantification. In any case, it is best to wrap it with a new type.
Strong recommendation: do not use bounding boxes for common data types. They will make your life harder, not easier. They do not bring anything.
Existential
{-
Universal
{-
You cannot have a generic or qualified type in an instance of a type class. So
instance Model (ContainerParams params) (Container model)
not allowed because type synonym expands to
instance Model (ContainerParams params) (forall ...)
In my GADT solution, I considered both param and model as parameters. This is because of something important: functional dependencies do not merge! And, the compiler does not assume that they will be confluent for type checking purposes. Functional dependencies are only useful for guiding the constraint resolver (thus, they resemble additional logical constructs, such as βcutsβ in the prologue). If you want a merge, use TypeFamilies .
class Model model where type Param model ...
Or an awesome way to do it
class (model ~ (TheModel param),param ~ (TheParam model)) => Model model param where type TheModel param type TheParam model
which has the same bidirectional as fundep. And, which then allows you to write your existential instance as
data Container model where Container :: Model model param => Container' model param -> Container model
and then you can do things like combining two containers with the same model type, knowing that the numerical value of params will match with existential precision. Using this, you can define
data HasParam model where HasParam :: Model model param => HasParam model data GADTContainer model where GADTContainer :: Model model param => Container' model param -> GADTContainer model newtype NewContainer model = NewContainer (forall param. Model model param => Container' model param)
and then the set (HasParam model, NewContainer model) provably isomorphic to the GADTContainer model , which also explains the relationship between these types
In any case, after you have taken care of this, you can only define your instance using the corresponding wrapped type.