The problem is pretty simple. I have a structure that looks something like this
data Foo = Foo [Bar]
data Bar = Boo | Moo Item Int
data Item = Item String Int
and I have a lens for changing the contents of the Item
s inside the data structure, such as this
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]]
The structure here isn't important, I just wanted to show an example that uses prisms and something deeply nested.
Now the problem is that I need the function passed to over
to be String -> IO String
, instead of just String -> String
. A similar thing to what I'm looking for here is something like mapM
, but with lenses. Is it possible to do something like this?
Lens provides the traverseOf
function, which is exactly like mapM
but takes a lens-like (it takes a traversal, which includes lenses and prims) over which you want to 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
So for your example, you can just use:
traverseOf (traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
There is also an operator version of traverseOf
, called %%~
.
If you're a bit familar with the representation of lenses inside the lens library, you might notice that traverseOf = id
! So, with this knowledge, you can rewrite the example to just:
(traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
(You even used traverse
which is just mapM
to build the traversal! lenses / prims are just like traverse
, but more specific.)
But this is just an aside, you might nevertheless want to use traverseOf
for clarity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With