My data types will always have at least two parameters, and the last two parameters are always 'q' and 'm', respectively:
{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances, TypeOperators, DataKinds, ConstraintKinds, FlexibleInstances #-}
data D1 q m = D1 q
data D2 t q m = D2 q
class Foo a where -- a has kind * -> *
f :: a x -> a x
class (Foo b) => Bar b where -- b has kind * -> *
-- the purpose of g is to change ONE type parameter, while fixing the rest
-- the intent of the equality constraints is to decompose the parameter b into
-- its base type and 'q' parameter, then use the same base type with a *different*
-- `q` parameter for the answer
g :: (b ~ bBase q1, b' ~ bBase q2) => b m -> b' m
instance (Foo (D2 t q), Integral q) => Bar (D2 t q) where
g (D2 q) = D2 $ fromIntegral q -- LINE 1
This program results in the error
Could not deduce (bBase ~ D2 t0) (LINE 1)
When I wrote the instance, I certainly intended bBase ~ D2 t
. I guess t is not bound somehow (hence the introduction of t0), and I don't know if GHC can deconstruct this type at all. Or maybe I'm just doing something silly.
More to the point, this kind of type equality/type deconstruction wouldn't be necessary if I make the parameter to Bar have kind * -> * -> *. But then I couldn't enforce the Foo constraint:
class (Foo (b q)) => Bar b where -- b has kind * -> * -> *
g :: b q m -> q b' -- this signature is now quite simple, and I would have no problem implementing it
This won't work because q is not a parameter to Bar, and I don't want it to a parameter to Bar.
I found a solution using TWO extra "dummy" associated types, but I don't really like having them around if I don't need them:
class (Foo b, b ~ (BBase b) (BMod b)) => Bar b where -- b has kind * -> *
type BBase b :: * -> * -> *
type BMod b :: *
g :: (Qux (BMod b), Qux q') => b m -> (BBase b) q' m
instance (Foo (D2 t q), Integral q) => Bar (D2 t q) where
type BBase (D2 t q) = D2 t
type BMod (D2 t q) = q
g (D2 q) = D2 $ fromIntegral q
This works, but it amounts to explicitly deconstructing the type, which I think should be unnecessary given the simple type of the instance.
I'm looking for a solution to either approach: either tell me how I can enforce a class constraint on a "more-applied" type, or tell me how to make GHC deconstruct types.
Thanks!
amy16.hs:7:1: Illegal equational constraint b ~ bBase q1 (Use -XGADTs or -XTypeFamilies to permit this) When checking the class method: g :: forall (bBase :: * -> * -> *) q1 (b' :: * -> *) q2 m. (b ~ bBase q1, b' ~ bBase q2) => b m -> b' m In the class declaration for
Bar' Failed, modules loaded: none.` So I think you need to add either theGADTs
language pragma or theTypeFamilies
pragma, and possibly some other pragmas as well. – mhwombat