8

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

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

我有一个镜头可以改变Item数据结构中 s 的内容,比如这个

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]]

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

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

4

1 回答 1

12

Lens 提供了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

(您甚至使用traversewhich 只是mapM为了构建遍历!lens / prims 就像traverse,但更具体。)

但这只是一个旁白,您可能仍然想使用traverseOf以清楚起见。

于 2014-07-01T16:31:34.523 回答