Consider the next example. I have a monad MyM that is just a StateT
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.State
import Control.Monad.Reader
type MyS = Int
type MyM = StateT MyS
Usually MyM is used for reading and writing MyS state, so I have functions like the next:
f1 :: (MonadState m, StateType m ~ MyS) => m ()
f1 = modify (+1)
But sometimes I need just to read MyS, so I want MonadReader context instead of MonadState:
f2 :: (MonadReader m, EnvType m ~ MyS) => m Int
f2 = liftM (+1) ask
And I want to write something like:
f3 :: (MonadState m, StateType m ~ MyS) => m Int
f3 = f1 >> f2
So basically I need every MonadState instance to be MonadReader instance too with the correspondent family type. Something like
instance MonadState m => MonadReader where
type EnvType m = StateType m
...
But I can’t find the way how to make it type check. Is it possible to express such the relation between MonadState and MonadReader?
Thanks.
It sounds like what you want is essentially
askto have the same effect asget. I can’t help but wonder why you don’t just usegetin that case 🙂If your aim is to write code that either reads the state or the env depending on what is available, you have to ask what you plan to do with, say,
ReaderT r (StateT s m) a, where you have both. For that reason, you can’t just do:because you’ll conflict with existing instances. You can, however, do:
Then if you have a polymorphic reader value like
f2, you can pull aMonadStateout of it withunRS. If you want to use some more devious extensions, try this withRankNTypes:Then you can do
useStateAsEnv (liftM (+1) ask)and get aMonadStatevalue. I haven’t thoroughly investigated how useful this is in practice, however – to produce a value of typeforall m. MonadReader m => m a, you can pretty much only useask,local, and monadic functions.Here’s a similar, less general but probably more useful thing, using standard transformers:
Edit: thinking about this later you could probably generalise this to any state monad transformer with
lift . runReaderT reader =<< get, but the type starts to be rather unwieldy:which is a generalisation of the above but may not actually be a useful one.