Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use `over` from Control.Lens but perform a monadic action and collect the results?

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 Items 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?

like image 489
Jakub Arnold Avatar asked Jul 01 '14 16:07

Jakub Arnold


1 Answers

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.

like image 108
bennofs Avatar answered Nov 02 '22 05:11

bennofs