如何使用镜头对某些状态下的序列执行只读monadic操作?

时间:2014-02-24 19:22:20

标签: haskell lens

我的数据结构或多或少看起来像这样(为了问题而简化)

data GameObject = GameObject { _num :: Int }
data Game = Game { _objects :: [GameObject] }

我使用makeLenses为这两者生成访问器。因此,我能够像这样进行操作 en masse

-- loop :: MonadState with Game inside
loop = objects.traversed.num += 1

这很棒。


但是,我无法找到相应的方法(每num打印一次):

game <- get
let objects = _objects game
let objectNums = map _num objects
mapM_ print objectNums

我尝试过像

这样的事情
uses (objects.traversed.num) >>= mapM_ print

但是我看到使用traversed和某些状态的唯一方法是使用monoids,我不想将字段组合在一起;我希望对来自我的状态的某些序列中元素中每个元素的一部分运行monadic动作。

那么,这是否可以使用一些提供的镜头功能,或者我必须自己编写,不知何故?

2 个答案:

答案 0 :(得分:6)

怎么样?
mapMOf_ (objects . traversed . num) print

或者如果你想将它应用于状态monad的状态,

get >>= mapMOf_ (objects . traversed . num) print

? (也许有一种更像镜头的方式将第一行与州monad的状态结合起来。如果是这样,我想亲自了解它。)

答案 1 :(得分:0)

此解决方案对镜头所看到的状态进行操作

objects . traverse . num .=>> putStrLn . ("num: " ++) . show

示例:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}

module Main where

import Control.Monad.IO.Class
import Control.Lens
--       (Getting, Lens', LensLike', lens, makeLenses, view, zoom)
import Control.Lens.Internal.Zoom -- (Focusing)
import Control.Monad.Trans.State.Lazy -- (StateT(StateT), execStateT)

viewM :: Monad m => Getting t s t -> (t -> m a) -> StateT s m a
viewM g f = StateT $ \s -> (, s) <$> f (view g s)

viewEachM ::
     Monad m => LensLike' (Focusing m c) t s -> (s -> m c) -> StateT t m c
viewEachM g f = zoom g $ id .=> f

infix 4 .=>, .=>>

(.=>) :: Monad m => Getting t s t -> (t -> m a) -> StateT s m a
g .=> f = viewM g f

(.=>>) :: Monad m => LensLike' (Focusing m c) t s -> (s -> m c) -> StateT t m c
g .=>> f = viewEachM g f

data GameObject = GameObject
  { _num :: Int
  } deriving (Show)

data Game = Game
  { _objects :: [GameObject]
  } deriving (Show)

makeLenses ''Game

makeLenses ''GameObject

defaultGame = Game {_objects = map GameObject [0 .. 3]}

action :: StateT Game IO ()
action = do
  id .=> putStrLn . ("game: " ++) . show
  objects .=> putStrLn . ("objects: " ++) . show
  objects .=> putStrLn . ("length of objects: " ++) . show . length
  -- use .=>> as alternative
  objects .=>> putStrLn . ("length of objects: " ++) . show . length
  -- if you use traverse you have to use .=>>
  objects . traverse . num .=>> putStrLn . ("num: " ++) . show

main :: IO ()
main = do
  execStateT action defaultGame
  return ()

<强>输出:

game: Game {_objects = [GameObject {_num = 0},GameObject {_num = 1},GameObject {_num = 2},GameObject {_num = 3}]}
objects: [GameObject {_num = 0},GameObject {_num = 1},GameObject {_num = 2},GameObject {_num = 3}]
length of objects: 4
length of objects: 4
num: 0
num: 1
num: 2
num: 3

解析器: lts-9.3

  • 镜头4.15.4
  • 变形金刚0.5