I was writing an application with a few Monads in a transformer stack. The top level application state resides in a TVar
, and various components of the application operate on parts of it.
I wrote a helper function to extract those parts:
hoistStateWithLens :: S.MonadState outerState m =>
Simple Lens outerState innerState ->
S.State innerState a ->
m a
hoistStateWithLens acc op = do
s <- S.get
let sp = s ^. acc
let (res, sp') = S.runState op sp
S.put (s & acc .~ sp')
return res
And I extract it from my main state as such:
runWebMState :: MonadTrans t => State appState a -> t (WebM appState) a
runWebMState x =
let
runWebMState_ :: State appState a -> WebM appState a
runWebMState_ f = do
appStateTVar <- ask
liftIO . atomically $ do
appState <- readTVar appStateTVar
let (fResult, appState') = runState f appState
writeTVar appStateTVar appState'
return fResult
in webM $ runWebMState_ x
Ensuring atomic access (multiple web handlers can run at once).
Using it in my application code gets pretty easy:
runRandom x = runWebMState $ hoistStateWithLens gen x
-- sample use of runRandom
get "/random" $ do
x <- runRandom $ state . randomR (1 :: Int,100)
text . pack . show $ x
I wanted to gather some feedback about the implementation and the idea itself. The whole project is OpenSource and available on GitHub.