Как я могу использовать Control.Lens для обновления i-го элемента списка?

У меня есть некоторые типы данных по строке

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

используя Control.Lens, я могу получить доступ к _bool i-го Inner (внутри монады State State), как это

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

Я также хотел бы обновить это значение с помощью

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

Однако (по моему мнению) функция 'to' создает только геттер, а не истинный объектив, который может использоваться как геттер, так и сеттер.

Итак, как я могу преобразовать (!! i) в объектив, который позволит мне обновить это поле?

Ответ 1

Вы не можете * превратить (!!) в любую объектоподобную вещь, отличную от Getter, но есть функция для выполнения такого рода вещей: ix, для доступа к вещам по индексам. На самом деле это Traversal, а не Lens, который здесь означает только то, что он может потерпеть неудачу (если индекс за пределами границ) - но пока индекс находится в списке, он будет работать.

Другая проблема, хотя - (^.), также является оператором, который используется исключительно для получения значений. Это несовместимо с, например, (%=), который воспринимает линзоподобную вещь как свой первый аргумент. И: (%=) предназначен для отображения функции над существующим значением; если вы просто хотите установить, вы можете использовать (.=). Поэтому вы, вероятно, хотите что-то вроде:

list . ix i . inner .= True

* На самом деле есть функция, которая может это сделать - она ​​называется upon - но она использует чудесную злую черную магию, и вы не должны ее использовать, по крайней мере, не для этого (и, вероятно, не для какого-либо реального кода).

Ответ 2

Используйте element, который является Traversal для указанного элемента списка:

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

Если вы хотите получить элемент списка, вы должны сделать это с помощью Maybe, поскольку элемент списка может отсутствовать:

myList :: Outer

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