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 an Item inside a data structure such as

 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 is not important here, I just wanted to show an example in which prisms and something deeply embedded are used.

Now the problem is that I need the function passed in over to be String -> IO String , and not just String -> String . A similar thing for what I'm looking for here is something like mapM , but with lenses. Is it possible to do something like this?

+6
source share
1 answer

The lens provides the traverseOf function, which is exactly like mapM , but accepts a lenticular (requires a traversal that includes lenses and primitives) over which you want a map .

 traverseOf :: Functor f => Iso stab -> (a -> fb) -> s -> ft traverseOf :: Functor f => Lens stab -> (a -> fb) -> s -> ft traverseOf :: Applicative f => Traversal stab -> (a -> fb) -> s -> ft 

So, for your example, you can simply use:

 traverseOf (traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos 

There is also a version of the traverseOf operator called %%~ .


If you are a little familiar with the representation of lenses inside the lens library, you may notice that traverseOf = id ! So, with this knowledge, you can rewrite the example simply:

 (traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos 

(You even used traverse , which is just mapM , to build a traversal! Lenses / primitives are similar to traverse , but more specific.)

But that's just aside, but you can use traverseOf for clarity.

+10
source

Source: https://habr.com/ru/post/971605/


All Articles