I am trying to define a function inside a type family which is polymorphic over a phantom type of a GADT itself defined within the type family.
My type family definition is along the lines of:
class Channel t where
    data Elem t a :: * 
    foo :: t -> Elem t a
    bar :: Elem t a -> [a]
I have an instance as follows:
data MyChannelType = Ch
instance Channel MyChannelType where
    data Elem MyChannelType a where
        MyConstructor :: Char -> Elem MyChannelType Char
    foo _ = MyConstructor 'a'
    bar (MyConstructor c) = repeat c
The compiler complains that:
Couldn't match type ‘a’ with ‘Char’
      ‘a’ is a rigid type variable bound by
          the type signature for foo :: MyChannelType -> Elem MyChannelType a
Is it possible to write this function with Rank2Types or to reformulate my data types to enable it?
EDIT : In response to the clarification Ganesh requested
I would want foo (bar Ch) :: [Int] to be illegal.
I have been using exactly the solution Ganesh suggests but I am motivated by the following more complicated example, where it falls down; given:
data MyOtherType = IntCh | StringCh
I have an instance as follows:
instance Channel MyOtherType where
    data Elem MyOtherType a where
        ElemInt    :: Int ->    Elem MyOtherType Int
        ElemString :: String -> Elem MyOtherType String
    foo IntCh    = ElemInt 0
    foo StringCh = ElemString "a"
    bar (ElemInt i)    = repeat i
    bar (ElemString s) = repeat s
Many thanks,
Michael
With the signatures you've given, foo is unimplementable for MyChannelType because it claims to be able to produce Elem MyChannelType a for any a type.
If what you really want is that there should only be one a type for a given t, you can use a type function to express this:
class Channel t where
    data Elem t a :: *
    type Contents t :: *
    foo :: t -> Elem t (Contents t)
    bar :: Elem t a -> [a]
and then add
type Contents MyChannelType = Char
to the instance.
In response to your edit, I would break up Channel into two classes:
class Channel t where
    data Elem t a :: *
    bar :: Elem t a -> [a]
class Channel t => ChannelContents t a where
    foo :: t -> Elem t a
You can then define the MyOtherType instances with:
instance Channel MyOtherType where
    data Elem MyOtherType a where
        ElemInt :: Int -> Elem MyOtherType Int
        ElemString :: String -> Elem MyOtherType String
    bar (ElemInt i) = repeat i
    bar (ElemString s) = repeat s
instance ChannelContents MyOtherType Int where
    foo IntCh = ElemInt 0
instance ChannelContents MyOtherType String where
    foo StringCh = ElemString "a"
You'll need to enable a few extensions: MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances (the latter two only because of the String instance).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With