Lens operators have useful variants that operate in stateful contexts. They are obtained by replacing ~
with =
in the operator name.
(+~) :: Num a => ASetter s t a a -> a -> s -> t
(+=) :: (MonadState s m, Num a) => ASetter' s a -> a -> m ()
Note: The stateful variants aren't expected to change the type, so they have the
Lens'
orSimple Lens'
signatures.
&
chainsIf lens-ful operations need to be chained, it often looks like this:
change :: A -> A
change a = a & lensA %~ operationA
& lensB %~ operationB
& lensC %~ operationC
This works thanks to the associativity of &
. The stateful version is clearer, though.
change a = flip execState a $ do
lensA %= operationA
lensB %= operationB
lensC %= operationC
If lensX
is actually id
, the whole operation can of course be executed directly by just lifting it with modify
.
Assuming this example state:
data Point = Point { _x :: Float, _y :: Float }
data Entity = Entity { _position :: Point, _direction :: Float }
data World = World { _entities :: [Entity] }
makeLenses ''Point
makeLenses ''Entity
makeLenses ''World
We can write code that resembles classic imperative languages, while still allowing us to use benefits of Haskell:
updateWorld :: MonadState World m => m ()
updateWorld = do
-- move the first entity
entities . ix 0 . position . x += 1
-- do some operation on all of them
entities . traversed . position %= \p -> p `pointAdd` ...
-- or only on a subset
entities . traversed . filtered (\e -> e ^. position.x > 100) %= ...