Deconstruction Type

My data types will always have at least two parameters, and the last two parameters are always equal to "q" and "m", respectively:

{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances, TypeOperators, DataKinds, ConstraintKinds, FlexibleInstances #-} data D1 qm = D1 q data D2 tqm = D2 q class Foo a where -- a has kind * -> * f :: ax -> ax 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) => bm -> b' m instance (Foo (D2 tq), Integral q) => Bar (D2 tq) where g (D2 q) = D2 $ fromIntegral q -- LINE 1 

This program produces an error

 Could not deduce (bBase ~ D2 t0) (LINE 1) 

When I wrote the instance, of course, I meant bBase ~ D2 t . I think t is not connected in some way (hence the introduction of t0), and I don't know if the GHC can deconstruct this type at all. Or maybe I'm just doing something stupid.

Moreover, this type of decomposition of type decomposition type would not be needed if I made the parameter for Bar look like * β†’ * β†’ *. But then I could not force the restriction Foo:

 class (Foo (bq)) => Bar b where -- b has kind * -> * -> * g :: bqm -> qb' -- this signature is now quite simple, and I would have no problem implementing it 

This will not work, because q is not a parameter for Bar, and I do not want the parameter to be in Bar.

I found a solution using TWO extra "dummy" related types, but I don't really like them 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') => bm -> (BBase b) q' m instance (Foo (D2 tq), Integral q) => Bar (D2 tq) where type BBase (D2 tq) = D2 t type BMod (D2 tq) = q g (D2 q) = D2 $ fromIntegral q 

This works, but it clearly explicitly deconstructs a type that, it seems to me, is not needed with a simple instance of the instance.

I’m looking for a solution for any approach: either tell me how I can force class restriction to a β€œmore applied” type, or tell me how to make GHC deconstruction types.

Thanks!

+7
source share
1 answer

From what you have described, you have the types b' :: * -> * -> * for which you want to restrict the applied b' t :: * -> * (for all t ).

As you summarize, you need to either deconstruct the type that is your attempt here starting with a b :: * -> * , the expected result of type application b = b' t , or to force the restriction on the "more applied" type from the starting point a b' :: * -> * -> * .

Type decomposition is not possible because the compiler does not know if b "deconstructed". In fact, this may not be the case, for example, I can create an instance of instance Bar Maybe , but Maybe cannot be deconstructed to type b' :: * -> * -> * and some type t :: * .

Starting with type b' :: * -> * -> * , restrictions on application b' can be moved to the body of the class, where variables are quantified:

  class Bar (b :: * -> * -> *) where g :: (Foo (b q1), Foo (b q2)) => b q1 m -> b q2 m 

For your example, there is another wrinkle: q1 and q2 can have their own limitations, for example, for an instance of D2 you need the Integral constraint. However, Bar corrects the constraints on q1 and q2 for all instances (in this case, the empty constraint). The solution is to use "type families with constraints" that allow instances to specify their own constraints:

  class Bar (b :: * -> * -> *) where type Constr bt :: Constraint g :: (Foo (b q1), Foo (b q2), Constr b q1, Constr b q2) => b q1 m -> b q2 m 

(enable {-# LANGUAGE ConstraintKinds #-} and import GHC.Prim )

Then you can write an instance of D2 :

  instance Bar (D2 t) where type Constr (D2 t) q = Integral q g (D2 q) = D2 $ fromIntegral q 
+1
source

All Articles