我如何使用Control.Lens中的`over`但执行monadic动作并收集结果?

Jak*_*old 8 haskell lenses haskell-lens

问题很简单.我的结构看起来像这样

data Foo = Foo [Bar]
data Bar = Boo | Moo Item Int
data Item = Item String Int
Run Code Online (Sandbox Code Playgroud)

我有一个镜头来改变Item数据结构内部的内容,比如这个

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]]
Run Code Online (Sandbox Code Playgroud)

这里的结构并不重要,我只是想展示一个使用棱镜和深度嵌套的例子.

现在的问题是,我需要传递给函数的overString -> IO String,而不是只String -> String.与我在这里寻找的东西类似mapM,但有镜头.可以这样做吗?

ben*_*ofs 12

镜头提供的traverseOf功能完全像,mapM但需要一个镜头(它需要一个遍历,包括镜头和prims)你想要的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
Run Code Online (Sandbox Code Playgroud)

所以对于你的例子,你可以使用:

traverseOf (traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
Run Code Online (Sandbox Code Playgroud)

还有一个运营商版本traverseOf,名为%%~.


如果您对镜头库中镜头的表现有点熟悉,您可能会注意到这一点traverseOf = id!因此,有了这些知识,您可以将示例重写为:

(traverse._Foo._Moo._1._Item._1) (... expression of type String -> IO String ...) foos
Run Code Online (Sandbox Code Playgroud)

(你甚至用traversemapM来构建遍历!镜头/ prims就像traverse,但更具体.)

但这只是一个旁边,你可能想要traverseOf清楚地使用它.