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

时间:2014-07-01 16:17:25

标签: haskell lens lenses

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

data Foo = Foo [Bar]
data Bar = Boo | Moo Item Int
data Item = Item String Int

我有一个镜头用于更改数据结构中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]]

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

现在问题是我需要传递给over的函数为String -> IO String,而不仅仅是String -> String。与我在这里寻找的类似的事情就像mapM,但有镜头。有可能做这样的事吗?

1 个答案:

答案 0 :(得分: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

因此,对于您的示例,您可以使用:

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来构建遍历!镜头/ prims就像traverse,但更具体。)

但这只是暂且不过,您可能希望使用traverseOf来表示清晰。