multistate: like mtl's ReaderT / WriterT / StateT, but more than one contained value/type.

[ bsd3, control, library ] [ Propose Tags ] [ Report a vulnerability ]

When using multiple Read/Write/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/MultiRWST can contain a heterogeneous list of values.

See the README for a longer description.


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
build-example

Build the MultiState-example example program

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.1, 0.1.2, 0.1.3.1, 0.1.3.2, 0.2.0.0, 0.3.0.0, 0.6.0.0, 0.6.1.0, 0.6.2.0, 0.7.0.0, 0.7.1.1, 0.7.1.2, 0.8.0.0, 0.8.0.1, 0.8.0.2, 0.8.0.3, 0.8.0.4
Change log changelog.md
Dependencies base (>=4.11 && <4.20), monad-control (>=1.0 && <1.1), mtl (>=2.1 && <2.4), tagged (>=0.7 && <0.9), transformers (>=0.3 && <0.7), transformers-base (<0.5) [details]
License BSD-3-Clause
Copyright Copyright (C) 2013 Jan Bracker, 2013-2020 Lennart Spitzner
Author Jan Bracker, Lennart Spitzner
Maintainer Lennart Spitzner <hexagoxel@hexagoxel.de>
Revised Revision 1 made by Bodigrim at 2023-10-01T08:35:02Z
Category Control
Home page https://github.com/lspitzner/multistate
Bug tracker https://github.com/lspitzner/multistate/issues
Source repo head: git clone git@github.com:lspitzner/multistate.git
Uploaded by lspitzner at 2022-01-24T20:50:24Z
Distributions Arch:0.8.0.4, Debian:0.8.0.3, LTSHaskell:0.8.0.4, NixOS:0.8.0.4
Reverse Dependencies 6 direct, 2 indirect [details]
Executables multistate-example
Downloads 20622 total (33 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2022-01-24 [all 1 reports]

Readme for multistate-0.8.0.4

[back to package description]

multistate

Build Status Hackage

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 Single-Valued Transformers

It is possible to run single-valued actions inside multi-valued transformers using the inflate functions. A function transforming a multi-valued transformer with exactly one element into a single-valued 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:

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

  2. 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, where s is the initial state, and t is t' with another state added.

  3. 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:

  1. 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.

  2. 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