有狀態的鏡頭
鏡頭操作符具有在有狀態上下文中執行的有用變體。它們是通過在運算子名稱中用 =
替換~
獲得的。
(+~) :: Num a => ASetter s t a a -> a -> s -> t
(+=) :: (MonadState s m, Num a) => ASetter' s a -> a -> m ()
注意:有狀態變體不會改變型別,因此它們具有
Lens'
或Simple Lens'
簽名。
擺脫 &
鏈
如果鏡頭操作需要連結,它通常看起來像這樣:
change::A -> A
change a = a & lensA %~ operationA
& lensB %~ operationB
& lensC %~ operationC
這要歸功於 &
的相關性。但有狀態版本更清晰。
change a = flip execState a $ do
lensA %= operationA
lensB %= operationB
lensC %= operationC
如果 lensX
實際上是 id
,那麼整個操作當然可以通過 modify
直接執行來直接執行。
具有結構化狀態的命令式程式碼
假設這個示例狀態:
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
我們可以編寫類似於經典命令式語言的程式碼,同時仍然允許我們使用 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) %= ...