如何使用Control.Lens更新列表的第i个元素?

时间:2013-06-09 05:15:22

标签: haskell lenses

我有一些

行的数据类型
data Outer = Outer { _list :: [ Inner ] }
data Inner = Inner { _bool :: Bool }

使用Control.Lens,我可以像这样访问第i个内部的_bool(在'State Outer'monad中)

boolValue <- gets (^. list . to (!! i) . inner)

我也想用

之类的东西来更新这个值
list ^. (to (!! i)) ^. inner %= True

然而(根据我的理解),'to'功能只会创建一个getter,而不是一个可以用作getter或setter的真镜头。

那么,我怎样才能将(!! i)转换为允许我更新此字段的镜头?

2 个答案:

答案 0 :(得分:17)

除了(!!)之外,你不能将Getter变成任何类似镜头的东西 - 但是有一个功能可以做这样的事情:ix,用于访问事物在指数。它实际上是Traversal,而不是Lens - 这里,只是意味着它可能失败(如果索引超出范围) - 但只要索引在列表中,它会起作用。

还有另一个问题 - (^.)也是一个专门用于获取值的运算符。它与例如不相容(%=),它将镜头般的东西作为第一个参数。并且:(%=)用于将函数映射到现有值;如果您只想设置,可以使用(.=)。所以你可能想要这样的东西:

list . ix i . inner .= True

*实际上有一个功能可以做到这一点 - 它被称为upon - 但它使用了奇妙的邪恶黑魔法而你不应该使用它,至少不是为了这个(并且可能不适用于此)真实的代码)。

答案 1 :(得分:9)

使用element,它是指定列表元素的Traversal

list . element i . inner %= True :: Outer -> Outer

如果要获取list元素,必须使用Maybe,因为list元素可能不在那里:

myList :: Outer

myList ^? list . element i . inner :: Maybe Bool