#haskell #haskell-lens #lenses
#haskell #haskell-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
для наглядности.