Как я могу использовать `over` из Control.Lens, но выполнять монадическое действие и собирать результаты?
Проблема довольно проста. У меня есть структура, которая выглядит примерно так
data Foo = Foo [Bar]
data Bar = Boo | Moo Item Int
data Item = Item String Int
и у меня есть линза для изменения содержимого Item
s внутри структуры данных, такой как эта
let foos = [Foo [Boo, Moo (Item "bar" 20) 10]]
over (traverse._Foo._Moo._1._Item._1) ("foo" ++) foos
-- which yields [Foo [Boo, Moo (Item "foobar" 20) 10]]
Структура здесь не важна, я просто хотел показать пример, который использует призмы и что-то глубоко вложенное.
Теперь проблема в том, что мне нужно передать функцию over
быть String -> IO String
вместо просто String -> String
, То, что я ищу здесь, похоже на mapM
, но с линзами. Можно ли сделать что-то подобное?
1 ответ
Объектив обеспечивает traverseOf
функция, которая точно так же, как mapM
но принимает линзоподобный (он требует обхода, который включает в себя линзы и примитивы), над которым вы хотите map
,
traverseOf :: Functor f => Iso s t a b -> (a -> f b) -> s -> f t
traverseOf :: Functor f => Lens s t a b -> (a -> f b) -> s -> f t
traverseOf :: Applicative f => Traversal s t a b -> (a -> f b) -> s -> f t
Так что для вашего примера вы можете просто использовать:
traverseOf (traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
Существует также версия оператора traverseOf
, называется %%~
,
Если вы немного знакомы с представлением линз внутри библиотеки линз, вы можете заметить, что traverseOf = id
! Итак, с этим знанием, вы можете переписать пример так:
(traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
(Вы даже использовали traverse
который просто mapM
построить обход! линзы / примы так же, как traverse
, но конкретнее.)
Но это только в стороне, вы все же можете использовать traverseOf
для ясности.