multistate
like mtl's ReaderT / WriterT / StateT, but more than one contained value/type.
https://github.com/lspitzner/multistate
LTS Haskell 22.25:  0.8.0.4@rev:1 
Stackage Nightly 20240616:  0.8.0.4@rev:1 
Latest on Hackage:  0.8.0.4@rev:1 
multistate0.8.0.4@sha256:38b2202d4474f3348e09fe31d9d2cdf1b40aba7c4e28f7270e5cc7b0bdd83c54,3730
Module documentation for 0.8.0.4
 Control
 Control.Monad
 Control.Monad.Trans
 Control.Monad
 Data
 Data.HList
multistate
Introduction
When using multiple Reader/Writer/State transformers in the same monad stack, it becomes necessary to lift the operations in order to affect a specific transformer. Using heterogeneous lists (and all kinds of GHC extensions magic), this package provides transformers that remove that necessity: MultiReaderT/MultiWriterT/MultiStateT can contain a heterogeneous list of values.
The type inferred for the getter/setter determines which value is read/written.
Example
simpleExample :: IO ()
simpleExample = runMultiStateTNil_  start with an empty state,
 i.e. :: MultiStateT '[] IO
$ withMultiStateA 'H'  "adding" a char to the state
$ withMultiStateA "ello, World!"  and a string
$ do  so:
 the monad here is MultiStateT '[String, Char] IO
let combinedPrint = do  no type signature necessary
c < mGet  type of mGet inferred to be m Char
cs < mGet  inferred to be m String
lift $ putStrLn (c:cs)
combinedPrint
mSet 'J'  we modify the Char in the state.
 again, the type is inferred,
 without any manual lifting.
combinedPrint
The output is:
Hello, World!
Jello, World!
( you can find both this and a more complex example in an executable in the package. )
Error Messages
If you try to execute an action that requires a specific type in the state, but the current state does not contain that type, the error message is something like
No instance for (Control.Monad.MultiState.ContainsType Foo '[]) x
where Foo
is the missing type.
Compatibility with SingleValued Transformers
It is possible to run singlevalued actions inside multivalued
transformers using the inflate
functions. A function transforming
a multivalued transformer with exactly one element into a
singlevalued transformer would be trivial, but it is currently not provided.
Naming Scheme
(Will refer to StateT in this paragraph, but equally valid for Reader/Writer)
The mtl monad transformers make use of primarily three methods to “unwrap”
a transformed value:
runStateT
, evalStateT
, execStateT
. These three all have a type
matching the pattern s > t m a > m b
, they differ in what b
is.
We will use a different naming scheme, for three reasons:

“run”, “eval” and “exec” are not in any way intuitive, and should be suffixed in any case.

For MultiStateT, it makes sense to transform an existing transformer, adding another state. The signature would be close to that of runStateT, only without the unwrapping part, i.e.
s > t m a > t' m b
, wheres
is the initial state, andt
ist'
with another state added. 
Sometimes you might want to add/run a single state, or a bunch of them. For example, when running an arbitrary StateT, you would need to provide a HList of initial states, and would receive a HList of final states.
Our naming scheme will instead be:

runStateT.*
unwraps a StateT. A suffix controls what exactly is returned by the function. There is a special version for when the list of states is Nil,runStateTNil
. 
withStateT.*
adds one or more states to a subcomputation. A suffix controls the exact return value.
withStates
/\
 withState withState .. withState v
StateT '[s, ..] m > StateT '[..] m > .. > StateT '[] m
 < 
 (withoutState) 
 
 
 runStateT runStateTNil 
\> m .. </
Specific functions are (constraints omitted):
runMultiStateT = runMultiStateTAS
runMultiStateTA :: HList s > MultiStateT s m a > m a
runMultiStateTAS :: HList s > MultiStateT s m a > m (a, s)
runMultiStateTSA :: HList s > MultiStateT s m a > m (s, a)
runMultiStateTS :: HList s > MultiStateT s m a > m s
runMultiStateT_ :: HList s > MultiStateT s m a > m ()
runMultiStateTNil :: MultiStateT '[] m a > m a
runMultiStateTNil_ :: MultiStateT '[] m a > m ()
withMultiState = withMultiStateAS
withMultiStateA :: s > MultiStateT (s ': ss) m a > MultiStateT ss m a
withMultiStateAS :: s > MultiStateT (s ': ss) m a > MultiStateT ss m (a, s)
withMultiStateSA :: s > MultiStateT (s ': ss) m a > MultiStateT ss m (s, a)
withMultiStateS :: s > MultiStateT (s ': ss) m a > MultiStateT ss m s
withMultiState_ :: s > MultiStateT (s ': ss) m a > MultiStateT ss m ()
withMultiStates = withMultiStatesAS
withMultiStatesAS :: HList s1 > MultiStateT (Append s1 s2) m a > MultiStateT s2 m (a, HList s1)
withMultiStatesSA :: HList s1 > MultiStateT (Append s1 s2) m a > MultiStateT s2 m (HList s1, a)
withMultiStatesA :: HList s1 > MultiStateT (Append s1 s2) m a > MultiStateT s2 m a
withMultiStatesS :: HList s1 > MultiStateT (Append s1 s2) m a > MultiStateT s2 m (HList s1)
withMultiStates_ :: HList s1 > MultiStateT (Append s1 s2) m a > MultiStateT s2 m ()
withoutMultiState :: MultiStateT ss m a > MultiStateT (s ': ss) m a
Known Deficits
This package currently lacks a complete set of “lifting instances”, i.e. instance definitions for classes such as mtl’s MonadWriter “over” the newly introduced monad transformers, as in
instance (MonadWriter w m) => MonadWriter w (MultiStateT c m) where ..
These “lifting instances” would be necessary to achieve full compatibility with existing transformers. Ping me if you find anything specific missing.
Changelog
See changelog.md
Changes
Changelog for multistate
package
0.8.0.4 January 2022
 Adapt for ghc9.0 and ghc9.2
 Clean up code a bit, fix compiler warnings (thanks sergv)
0.8.0.3 May 2020
 Adapt for ghc8.10
 Add nixexpressions for testing against different ghc versions
 Drop support for ghc < 8.4
0.8.0.2 June 2019
 Adapt for ghc8.8 (optimistically; QuickCheck does not build so tests are untested)
0.8.0.1 October 2018
 Adapt for ghc8.6 (really, this time)
 Make package Wcompatible
0.8.0.0 April 2018
 Adapt for ghc8.4
 Drop support for ghc<8.0
 Add class
MonadMultiGet
that roughly translates to “any read access” (instances for Reader and State)  Add datatype
MultiGST
that has a single taggified HList instead of the three r, w, s lists withMultiRWS
0.7.1.2 August 2017

Adapt for ghc8.2

Minor strictness fix for MultiRWS
0.7.1.1 May 2016
 Adapt for ghc8
0.7.1.0 March 2016
 Add new method
withoutMultiFoo
, inverse ofwithMultiFoo
0.7.0.0 February 2016

Add instances:
 MonadIO
 Alternative
 MonadPlus
 MonadBase
 MonadTransControl
 MonadBaseControl
0.6.2.0 June 2015
 Add MonadFix instances
0.6.1.0 June 2015
 Export classes from transformer modules
0.6.0.0 June 2015

Add
MultiRWST

Add inflate functions (e.g.
StateT _ > MultiStateT _
) 
Improve lazyness

Move changelog from
README.md
tochangelog.md
0.5.0.0 March 2015

Breaking changes (!):
Refactor some parts of the interface, see “naming scheme” in the README; The changes are:
old new withMultiFoo
withMultiFooA
withMultiFoos
withMultiFoosA
mAskRaw
mGetRaw
mPutRaw
evalMultiStateT
runMultiStateTNil
evalMultiStateTWithInitial
runMultiStateTA
evalMultiReaderT
runMultiReaderTNil
evalMultiReaderTWithInitial
runMultiReaderTA
execMultiWriterT
runMultiWriterTW

Start using hspec; Add proper cabal testsuite.
0.4.0.0: March 2015

Refactor from
Control.Monad.*
toControl.Monad.Trans.*

Put classes (
MonadMulti*
) into separate modules 
Add Strict and Lazy variants

Deprecate previous modules
0.3.0.0 January 2015

Add
MultiWriter

Fixity for
(:+:)

support ghc7.10
0.2.0.0 January 2015

Start using DataKinds and TypeOperators to make the HList representation more readable. The translation roughly is:
Null > '[] Cons a Null > '[a] Cons a b > a ': b TNull > HNil TCons a b > a :+: b

Remove dependency on
tfp
package.
0.1.3.2 September 2014

Add example

Clean up / Add dependencies

More documentation
0.1.2 September 2014

Expose
HList
module 
Add haddocks
0.1.1 June 2014
 First version published on hackage