Cheatsheet: Stacking the State and Either Monads
I do this rarely enough that I always have to Google for how to do it, which is a good sign that I should just write it down.
Before running the code below, install
cabal install mtl works.
Here, I’ll use
Stored as the type of the state stored in the state monad and
Err as the type of the error. I could have picked any types for these two.
import Control.Monad.State (StateT, modify, get, put, lift, evalStateT) import Data.Map (Map) import qualified Data.Map as Map type Stored = Map String Int type Err = String type StoreM = StateT Stored (Either Err) note :: a -> Maybe b -> Either a b note msg = maybe (Left msg) Right save :: String -> Int -> StoreM () save k v = modify (Map.insert k v) load :: String -> StoreM Int load k = do store <- get lift $ note ("the key " ++ k ++ " is missing") (Map.lookup k store) stateManip :: StoreM Int stateManip = do save "x" 1 save "x" 2 save "y" 123 x <- load "x" y <- load "y" save "z" (x + y) load "z" main = putStrLn $ show $ evalStateT stateManip Map.empty
StoreM is the monad type.
evalStateT runs your monadic function (
stateManip) with some starting state (
Map.empty, in this case).
I’ve defined two other functions in that monad:
save function modifies the state, and it always succeeds. The
load function reads the state (but doesn’t modify it), and may fail. Both of these are implemented in terms of functions provided by the monad library.
I use a helper function called
note to convert
Left Err by adding an error message, but this doesn’t really have much to do with the monad. It’s just for convenience.
evalStateT, the key functions from
modify (which modifies the state value by applying some function to it),
get (which gives you the current state),
put (which replaces the current state), and
lift (which “converts” a function that doesn’t use state into one that does).