Question

Pleease see the use of liftIO in the function increment below. I have a hunch that there's a better way to handle it using lenses. Any suggestions?

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens
import Control.Monad.IO.Class
import Control.Monad.State

data PersistentCounter = PersistentCounter {
    _cValue :: Int,
    _cFilename :: FilePath
  } deriving Show
makeLenses ''PersistentCounter

increment :: StateT PersistentCounter IO ()
increment = do
  t <- cValue <+= 1
  f <- use cFilename
  liftIO $ writeFile f $ show t

EDIT: I thought there might be a mechanism something like `zoom2, below.

increment :: StateT PersistentCounter IO ()
increment = do
  t <- cValue <+= 1
  zoom2 store 

store :: PersistentCounter -> IO ()
store counter =
  writeFile (counter^.cFilename) $ show (counter^.cValue)

-- | Invoke a function in the inner monad, and pass the state as
--   a parameter.
zoom2 :: Monad m => (s -> m ()) -> StateT s m ()
zoom2 f = get >>= (\s -> lift (f s)) >> return ()
Was it helpful?

Solution

I've written (almost) zoom2 many times before---I usually call it getsM. A generic version looks like

-- from Control.Monad.State
gets  :: MonadState s m => (s ->   a) -> m a
getsM :: MonadState s m => (s -> m a) -> m a
getsM f = state  $ \s -> (,s) <$> f s

Or if you don't mind fixing a particular stack

-- from Control.Monad.Trans.State
gets  :: Monad m => (s ->   a) -> StateT s m a
getsM :: Monad m => (s -> m a) -> StateT s m a 
getsM f = StateT $ \s -> (,s) <$> f s 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top