问题很简单。我的结构看起来像这样
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
,但有镜头。有可能做这样的事吗?
答案 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
来表示清晰。