I am experimenting with an mtl-style class that allows me to lift Pipe composition over an outer monad. To do so, I must define which two variables of the type are the domain and codomain of Pipe composition.
I tried using an associated type family approach, but to no avail:
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.Trans.Free
import Control.Monad.Trans.State
import Control.Pipe hiding (Pipe)
data Pipe a b m r = Pipe { unPipe :: FreeT (PipeF a b) m r }
class MonadPipe m where
type C a b (m :: * -> *) :: * -> *
idT :: C a a m r
(<-<) :: C b c m r -> C a b m r -> C a c m r
instance (Monad m) => MonadPipe (Pipe i o m) where
type C a b (Pipe i o m) = Pipe a b m
idT = Pipe idP
(Pipe p1) <-< (Pipe p2) = Pipe (p1 <+< p2)
instance (MonadPipe m) => MonadPipe (StateT s m) where
type C a b (StateT s m) = StateT s (C a b m)
idT = StateT $ \s -> idT
(StateT f1) <-< (StateT f2) = StateT $ \s -> f1 s <-< f2 s
However, the above code does not type-check. GHC gives the following errors:
family.hs:23:15:
Could not deduce (C a a m ~ C a0 a0 m0)
from the context (MonadPipe m)
bound by the instance declaration at family.hs:21:14-52
NB: `C' is a type function, and may not be injective
Expected type: C a a (StateT s m) r
Actual type: StateT s (C a0 a0 m0) r
In the expression: StateT $ \ s -> idT
In an equation for `idT': idT = StateT $ \ s -> idT
In the instance declaration for `MonadPipe (StateT s m)'
family.hs:24:10:
Could not deduce (C b c m ~ C b0 c0 m1)
from the context (MonadPipe m)
bound by the instance declaration at family.hs:21:14-52
NB: `C' is a type function, and may not be injective
Expected type: C b c (StateT s m) r
Actual type: StateT s (C b0 c0 m1) r
In the pattern: StateT f1
In an equation for `<-<':
(StateT f1) <-< (StateT f2) = StateT $ \ s -> f1 s <-< f2 s
In the instance declaration for `MonadPipe (StateT s m)'
<<Two other errors for 'C a b m' and 'C a c m'>>
It’s hard for me to understand why the types won’t unify, especially for the idT definition, since I’d expect the inner idT to be universally quantified over a so it would match the outer one.
So my question is whether this is implementable with type families, and if not possible with type families, how could it be implemented?
EDITED Three times: see bottom for data family version. And changed GADT version to drop m.
Let me guess: leftovers?
Let me walk myself through the type error first, the a SOLUTION.
Your previous
newtypeway to make the Category instances probably works here if the type family becomes a data family.EDIT: You alter the order of parameters and newtype StateT to solve it:
Though MonadTrans might now be sad. Another approach keeps the argument order by using a GADT to, perhaps, more cleanly express what you are trying to build:
And I can translate this into a perhaps even nicer data family?