在StateT中使用镜头操作内部monad

时间:2013-11-25 11:11:28

标签: haskell lens

请注意在下面的liftIO函数中使用increment。我有一种预感,有一种更好的方法来处理它使用镜头。有什么建议吗?

{-# 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

编辑:我认为可能会有类似`zoom2的机制。

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 ()

1 个答案:

答案 0 :(得分:2)

我以前写过(几乎)zoom2多次 - 我通常称之为getsM。通用版本看起来像

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

或者,如果您不介意修复特定堆栈

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