假设我有一个嵌套结构如下:
data Bar = Bar { _id :: Integer, _bars :: [Bar] }
data Foo = Foo { _bars :: [Bar] }
我有一个
Foo
有一堆 Bars
与各种id
年代:foo = Foo [Bar 1 [Bar 2], Bar 3 [Bar 4, Bar 5]]
怎么样,也许是用镜头,我修改
foo
这样Bar 5
变成 Bar 6
?我知道我使用
fclabels
做这样的事情:mkLabel ''Foo
mkLabel ''Bar
modify bars (\bars -> ...) foo
但是条形可以无限嵌套。如何定位和修改
Bar
有指定的ID?
最佳答案
是的,lens
可以做到这一点。 Control.Lens.Plated
模块包含用于“废弃您的样板”式编程的工具,具有自相似结构,如您的 Bar
.这个想法非常简单:你解释了如何找到一个节点的直接子节点(通过编写 Traversal' a a
),然后库递归地将该遍历应用于整个结构。
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Bar = Bar { _lbl :: Int, _bars :: [Bar] } deriving (Show)
makeLenses ''Bar
instance Plated Bar where
plate = bars.traverse
(如果您不想自己实现
plate
,可以推导出 Data
并将 instance
留空。)transform :: Plated a => (a -> a) -> a -> a
接受一个修改单个节点并将其应用于整个结构的函数。fiveToSix :: Bar -> Bar
fiveToSix = transform go
where go bar
| bar^.lbl == 5 = bar & lbl .~ 6
| otherwise = bar
使用您问题中的示例:
ghci> let bars = [Bar 1 [Bar 2 []], Bar 3 [Bar 4 [], Bar 5 []]]
ghci> map fiveToSix bars
[Bar 1 [Bar 2 []], Bar 3 [Bar 4 [], Bar 6 []]]
再举一个例子,对于 funzies,让我们使用
cosmos
拉所有Bar 5
来自 Bar
.fives :: Bar -> [Bar]
fives = toListOf $ cosmos.filtered (views lbl (== 5))
关于haskell - 按值修改 Haskell 嵌套记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42516064/