13

我有一些数据类型

data Outer = Outer { _list :: [ Inner ] }
data Inner = Inner { _bool :: Bool }

使用 Control.Lens,我可以像这样访问第 i 个内部(在“状态外部”单子内)的 _bool

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

我也希望能够用类似的东西更新这个值

list ^. (to (!! i)) ^. inner %= True

但是(根据我的理解),“to”函数只创建一个 getter,而不是一个可以用作 getter 或 setter 的真正镜头。

那么,如何将 (!! i) 转换为允许我更新此字段的镜头?

4

2 回答 2

17

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

不过,还有另一个问题——(^.)也是一个专门用于获取值的运算符。它与 eg 不兼容(%=),后者将类似镜头的事物作为其第一个参数。和:(%=)用于将函数映射到现有值;如果你只想设置,你可以使用(.=). 所以你可能想要这样的东西:

list . ix i . inner .= True

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

于 2013-06-09T05:33:26.000 回答
9

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

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

如果要获取列表元素,则必须使用 a 这样做,Maybe因为列表元素可能不存在:

myList :: Outer

myList ^? list . element i . inner :: Maybe Bool
于 2013-06-09T05:26:30.937 回答