Haskell镜头:对遍历应用转换

时间:2017-12-21 20:42:00

标签: haskell traversal lens

使用Haskell镜头库时,我遇到了以下问题。

让我使用Gabriel Gonzalez撰写的"Program imperatively using Haskell lenses"博客文章中的例子。

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Game = Game
    { _score :: Int
    , _units :: [Unit]
    , _boss  :: Unit
    } deriving (Show)

data Unit = Unit
    { _health   :: Int
    , _position :: Point
    } deriving (Show)

data Point = Point
    { _x :: Double
    , _y :: Double
    } deriving (Show)

makeLenses ''Game
makeLenses ''Unit
makeLenses ''Point

这是一个很好的玩具结构。具体来说,这是一个实例化:

gameState :: Game
gameState = Game
    { _score = 0
    , _units =
        [ Unit
            { _health = 10
            , _position = Point { _x = 3.5, _y = 7.0 }
            }
        , Unit
            { _health = 15
            , _position = Point { _x = 1.0, _y = 1.0 }
            }
        , Unit
            { _health = 8
            , _position = Point { _x = 0.0, _y = 2.1 }
            }
        ]
    , _boss = Unit
        { _health = 100
        , _position = Point { _x = 0.0, _y = 0.0 }
        }
    }

查询单位的工作方式如下:

gameState ^.. units . traverse . health    -- health of all units is [10,15,8]

可以像这样

更改所有单位的运行状况
gameState & units . traverse . health .~ 100 

这里我被卡住了:假设我想对单位的遍历应用转换,即获得所有单位的健康状况,根据某些规则更改所有单位,并设置它们。例如,如果我想要反转健康列表(在这个例子中不是一个非常有意义的转换),这样的事情应该有效:

gameState & units . traverse . health %~ reverse 

但我无法确定使用哪个运营商。

我使用此示例作为指导(排序和替换所有元素< 4)

[2,4,3,5,4,6,1] & partsOf (traverse. filtered (<4)) %~ sort  -- is [1,4,2,5,4,6,3]

1 个答案:

答案 0 :(得分:3)

对于这种特殊的转变,我认为你正在寻找:

gameState & partsOf (units.traverse.health) %~ reverse

光学:

units.traverse.health

代表_health内每个Unit Unit的遍历(按_units字段中显示Game的顺序)。这与镜头(不是遍历!)不同,它关注健康列表[Int]中的Game - 为了得到这个,您需要将partsOf应用于遍历

如果 - 如Control.Lens.Traversal模块中所述,您可以直接使用遍历 - 您希望遍历结构,使用monadic或applicative side effects(按从左到右的顺序应用)更改其内容。但是,如果您希望将整个列表作为一个整体进行操作并使用常规[Health] -> [Health]转换,则需要先将遍历转换为partsOf镜头。