Consider the following code:
type CFunctor f = forall x y. (x -> y -> Constraint) -> f x -> f y -> Constraint
type MapList :: CFunctor []
class MapList c xs ys
instance MapList c '[] '[]
instance (c x y, MapList c xs ys) => MapList c (x ': xs) (y ': ys)
This works fine, but it's desirable in some situations to "make things compute" by introducing a functional dependency of the form:
class MapList c xs ys | c xs -> ys
With the functional dependency we have the following code:
type CFunctor f = forall x y. (x -> y -> Constraint) -> f x -> f y -> Constraint
type MapList :: CFunctor []
class MapList c xs ys | c xs -> ys
instance MapList c '[] '[]
instance (c x y, MapList c xs ys) => MapList c (x ': xs) (y ': ys)
This does not compile however, and produces the following error on the last instance:
[typecheck] [E] • Illegal instance declaration for ‘MapList c (x : xs) (y : ys)’
The liberal coverage condition fails in class ‘MapList’
for functional dependency: ‘c xs -> ys’
Reason: lhs types ‘c’, ‘x : xs’
do not jointly determine rhs type ‘y : ys’
Un-determined variable: y
• In the instance declaration for ‘MapList c (x : xs) (y : ys)’
This makes sense: c + xs determines ys due to the recursive use of MapList c xs ys (which has a functional dependency). But c + x ': xs determines c + y ': ys only if x determines y, which is a property we must require of the class being passed in for c.
But how can we adjust the CFunctor kind to demand this? As far as I'm aware there is no vocabulary in kind signatures to discuss functional dependencies. Is there still a way I can make this work?