3
votes

Given that I have the data types

data A a = A a
data B b = B b

and the type class

class C c where
    f :: c a -> a

Now the class C expects a type of kind * -> *, so I can do

instance C A where
    f (A a) = a

instance C B where
    f (B b) = b

Now given a function such as:

ab :: b -> A (B b)
ab = A . B

How would I declare an instance of C for the result type?

I first through I might be able to use a type synonym with TypeSynonymInstances, like this:

type AB b = A (B b)
class C AB where
    f (A (B b)) = b

Especially since ghci reports the correct kind:

*Main> :k AB
AB :: * -> *

However, you can't seem to use partially applied type synonyms in instance declarations (or anywhere, for that matter).

Is there some way I can compose the types A and B like I can compose the constructors, or some other syntax for declaring an instance for such a nested type?

3

3 Answers

8
votes

The simplest is probably to define

newtype AB a = AB { unAB :: A (B a) }

GHC should be able to derive a C instance for this.

You could also use TypeCompose to accomplish this,

type AB = A :. B

instance C AB where
  f = f . f . unO
2
votes

There is no way to declare such an instance. In fact, allowing arbitrary type functions such as AB in instance declarations makes instance resolution undecidable.

Usually, the solution is to declare a newtype. Of course, you will have to explicitly convert to and from the newtype when you want to use the class.

newtype AB a = AB (A (B a))
instance C AB where f (AB (A (B a)) = a

If your usage of the class is simple enough, you may want to just pass the overloaded method as a parameter of functions that use it.

1
votes

You have to use a newtype. A type synonym must always be applied completely, as it is only a synonym, not a lambda. A newtype has no runtime penalty; it will be removed when compiling.