Why are some styles prefixed with "Monad"?
After reading some code, I sometimes encounter classes with the prefix Monad
, examples of which include MonadState
, MonadIO
, MonadReader
and etc.
What is their purpose?
Taking MonadState
as an example, I know that
-
State
allows stateless state -
StateT
allows other monads to be used, for exampleIO
to "combine" the functionality of both
But MonadState
lets exactly?
I don't need another group of identically named types / types, can someone explain?
source to share
State
, StateT
and other types such as these are related to transformers
, while the class MonadState
and other classes such as it comes from mtl
. Note that the former are types and the latter are types.
Types from transformers
are instances of Monad
and MonadTrans
. You can work with them directly, but there are a few annoyances:
-
If you have multiple stacks with multiple layers, you have to sprinkle your code with a lot of calls
lift
to access the functions of each layer. -
Sometimes two different types provide the same "interface". For example, both
RWST
andReaderT
offer reading features such asask
. When writing a function, it is annoying to have to do one from the other, reducing generality.
Scripting Monad*
from mtl
makes these problems easier:
-
They have "end-to-end" instances that eliminate many of the calls
lift
(or rather, handle them automatically). For exampleStateT
is an instanceMonadState
, butReaderT
over isStateT
also an instanceMonadState
, so you can use directlyget
.import Control.Monad import Control.Monad.Reader import Control.Monad.State -- put the environment in the state bar :: ReaderT Int (State Int) () bar = ask >>= lift . put -- we use lift here barMTL :: ReaderT Int (State Int) () -- This ONLY works if we have imported the -- required instances from mtl. -- The MonadState instance for ReaderT, in particular. barMTL = ask >>= put -- the put is auto-lifted
-
You can put a constraint
Monad*
in your functions and work with the interfaceMonad*
"instead of immediately executing a specific implementation of the monad. Thus, your functions become more general, and the choice of the exact" implementation "is delayed until the last moment.import Control.Monad import Control.Monad.State import Control.Monad.RWS -- Dumb function that increments the state. -- Doesn't commit to a specific implementation of the monad. baz :: MonadState Int m => m () baz = modify succ main :: IO () main = do -- run as State print $ runState baz 0 -- run as RWS print $ runRWS (baz >> tell ()) () 0
source to share