如何在执行单子操作并收集结果的情况下使用 Control.Lens 中的 `over` 函数?

8
问题很简单。我有一个类似于这样的结构:
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一样的东西,但是使用的是lens。有可能做到这样吗?

1个回答

12
镜头提供了 traverseOf 函数,它类似于 mapM,但接受一个像镜头一样的遍历器(它接受一个遍历器,其中包括镜头和原语),你可以使用它来执行 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是相似的。镜头/基元就像 traverse 一样,但更具体。
但是,为了清晰起见,您可能仍然想要使用traverseOf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接