I'm trying to express an idea that given
instance (MonadTrans t, MonadX m) => MonadX (t m)
it should follow, that any t1 (t2 ... (tn m)) is also MonadX as long as all tx have MonadTrans instance. However when i try to write it down it doesn't work:
{-# LANGUAGE BasicallyEverything #-}
data Dict c where
Dict :: c => Dict c
class (forall m . Monad m => Monad (t m)) => MonadTrans t where
lift :: Monad m => m a -> t m a
class (c m, Monad m, forall t . (MonadTrans t, Monad (t m)) => c (t m)) => Foo c m
instance (c m, Monad m, forall t . (MonadTrans t, Monad (t m)) => c (t m)) => Foo c m
liftDict :: MonadTrans t => Dict (Foo c m) -> Dict (Foo c (t m))
liftDict Dict = Dict
This results in following error:
• Could not deduce: c (t1 (t m)) arising from a use of ‘Dict’
from the context: MonadTrans t
bound by the type signature for:
liftDict :: forall (t :: (* -> *) -> * -> *)
(c :: (* -> *) -> Constraint) (m :: * -> *).
MonadTrans t =>
Dict (Foo c m) -> Dict (Foo c (t m))
at src/Lib.hs:85:1-64
or from: Foo c m
bound by a pattern with constructor:
Dict :: forall (a :: Constraint). a => Dict a,
in an equation for ‘liftDict’
at src/Lib.hs:86:10-13
or from: (MonadTrans t1, Monad (t1 (t m)))
bound by a quantified context at src/Lib.hs:1:1
• In the expression: Dict
In an equation for ‘liftDict’: liftDict Dict = Dict
• Relevant bindings include
liftDict :: Dict (Foo c m) -> Dict (Foo c (t m))
(bound at src/Lib.hs:86:1)
|
86 | liftDict Dict = Dict
Is there any way to make it work?
You get the exact same error with the slightly simpler definition of
Foo c mgiven here:Let's clarify some of the variable names, so that it's clear which variables are definitely equal and which aren't when writing
liftDict.Have:
Want:
In the "want" category,
c (t m)is easy -- we applyforall t'. MonadTrans t' => c (t' m)to ourt'~tandMonadTrans t. TheMonad (t m)is easy, too, by applyingforall m'. Monad m' => Monad (t m')tom'~mandMonad m.But that last one... is tricky! You want these to line up:
But they don't quite line up. What's happened here is that
mis a fixed monad, not an argument that we can specialize to our new choice oft m! Okay, so let's make it an argument.This compiles! But it no longer says what we wanted, because the inductive step we've said here doesn't demand a
cconstraint. Luckily, we can add that back in without causing it to stop compiling:I think you might find it intuitive to group these constraints slightly differently:
But beware: this
Fooprobably has fewer instances than you think at first. In particular, for there to be aFoo cinstance, there can be only one, fully general, instance ofcfor type-level applications.