In general, I'm wondering if there's a way to write a generic fold that generalizes a function that applies a forall type like:
f :: forall a. Data (D a) => D a -> b
given some datatype D for which instance Data (D a) (possibly with constraints on a). To be concrete, consider something even as simple as False `mkQ` isJust, or generally, a query on the constructor of a higher-kinded datatype. Similarly, consider a transformation mkT (const Nothing) that only affects one particular higher-kinded type.
Without explicit type signatures, they fail with No instance for Typeable a0, which is probably the monomorphism restriction at work. Fair enough. However, if we add explicit type signatures:
t :: GenericT
t = mkT (const Nothing :: forall a. Data a => Maybe a -> Maybe a)
q :: GenericQ Bool
q = False `mkQ` (isJust :: forall a. Data a => Maybe a -> Bool)
instead we are told that the forall type of the outer signatures are ambiguous:
Could not deduce (Typeable a0)
arising from a use of ‘mkT’
from the context: Data a
bound by the type signature for:
t :: GenericT
The type variable ‘a0’ is ambiguous
I can't wrap my head around this. If I'm really understanding correctly that a0 is the variable in t :: forall a0. Data a0 => a0 -> a0, how is it any more ambiguous than in say mkT not? If anything, I would've expected mkT to complain because it is the one that interacts with isJust. Additionally, these functions are more polymorphic than the branching on concrete types.
I'm curious to know if this is a limitation of proving the inner constraint isJust :: Data a => ... — my understanding is that any type of instance Data inhabited with Maybe a must also have Data a to be valid by the instance constraint instance Data a => Data (Maybe a).
tldr: You need to create a different function.
mkThas the following signature:And you want to apply it to a polymorphic function of type
(forall x. Maybe x -> Maybe x). It is not possible: there is no way to instantiateain(a -> a)to obtain(forall x. Maybe x -> Maybe x).It's not just a limitation of the type system, the implementation of
mkTwouldn't support such an instantiation either.mkTsimply compares concrete typesaandbfor equality at run time. But what you want is to be able to test whetherbis equal toMaybe xfor somex. The logic this requires is fundamentally more involved. But it is certainly still possible.Below,
mkT1first matches the typebagainst theApppattern to know whetherbis some type applicationg y, and then tests equality ofgandf:Compilable example with
mkQ1as well: