I have an associated type family Bar in a type class Foo. Foo' requires that Bar f ~ Bar (f a) for all a, but in another function test I get an error Couldn't match type 'Bar f' with 'Bar (f a)', even though it depends on Foo' f. The code:
{-# LANGUAGE TypeFamilies, PolyKinds, QuantifiedConstraints #-}
import Data.Proxy
class Foo f where
type Bar f
class (Foo f, forall a. Foo (f a), forall a. Bar f ~ Bar (f a)) => Foo' f where
test :: Foo' f => Proxy (f a) -> Bar (f a) -> Bar f
test _ = id
Why can't GHC figure out this type equality and is there a way to help the typechecker without asserting the concrete equality in test?
Type families and quantified constraints don't mix. Much even less with type equalities. But there is a way to keep them separate to achieve more or less the same result.
A quantified constraint is actually restricted in the ways it can be used. It's not sufficient to know that a constraint holds for all types, you also need a way to know which types you need to specialize it to. GHC achieves this by representing quantified constraints as a sort of "local instance" subject to the common rules of instance resolution. I'm not sure quantified type equalities even mean anything. In summary, it's not enough that a quantified constraint can be instantiated to do what we want; that instantiation must happen in the regular course of instance resolution, and that puts restrictions on the allowed forms of quantified constraints.
The trick is to define a class synonym for the body of the quantified constraint:
That way we can rewrite the quantified constraint as
forall x. EBar f x, no type families in sight:To use this class, we need an explicit function to specialize the quantified constraint (the problem is that if we use an equality
Bar f ~ Bar (f a)directly, the type checker can't relate that to the quantified constraintforall x. EBar f x, which looks nothing like it):