[英]How can I express `mapM` with `concat` using Lenses to concatenate results of an IO operation?
I'm trying to figure out a way how to combine traverseOf
with >>=
in such a way that would allow the following. 我试图弄清楚如何将
traverseOf
与>>=
组合在一起,以便允许以下方式。
TLDR; TLDR; A simple example in plain Haskell would be something like this, but using lenses deep inside a data structure.
普通Haskell中的一个简单例子就是这样,但在数据结构内部使用镜头。
λ> fmap concat $ mapM ((return :: a -> IO a) . const ["he", "he"]) ["foo", "bar", "baz"]
["he","he","he","he","he","he"]
Here's a lengthy explanation with examples 这是一个冗长的例子说明
data Foo = Foo [Bar] deriving Show
data Bar = Baz | Qux Int [String] deriving Show
makePrisms ''Foo
makePrisms ''Bar
items :: [Foo]
items = [Foo [Baz], Foo [Qux 1 ["hello", "world"], Baz]]
-- Simple replacement with a constant value
constReplace :: [Foo]
constReplace = over (traverse._Foo.traverse._Qux._2.traverse) (const "hehe") items
-- λ> constReplace
-- [Foo [Baz],Foo [Qux 1 ["hehe","hehe"],Baz]]
-- Doing IO in order to fetch the new value. This could be replacing file names
-- with the String contents of the files.
ioReplace :: IO [Foo]
ioReplace = (traverse._Foo.traverse._Qux._2.traverse) (return . const "hehe") items
-- λ> ioReplace
-- [Foo [Baz],Foo [Qux 1 ["hehe","hehe"],Baz]]
-- Replacing a single value with a list and concatenating the results via bind
concatReplace :: [Foo]
concatReplace = over (traverse._Foo.traverse._Qux._2) (>>= const ["he", "he"]) items
-- λ> concatReplace
-- [Foo [Baz],Foo [Qux 1 ["he","he","he","he"],Baz]]
-- Same as the previous example, but the list comes from an IO action
concatIoReplace :: IO [Foo]
concatIoReplace = (traverse._Foo.traverse._Qux._2) (return . (>>= const ["he", "he"])) items
-- λ> concatIoReplace
-- [Foo [Baz],Foo [Qux 1 ["he","he","he","he"],Baz]]
Now the last example is where the problem is, because I've cheated a little bit by changing around the function that's being applied. 现在最后一个例子是问题出在哪里,因为我通过改变正在应用的函数来作弊。 In the
concatReplace
I was able to use >>=
(thanks to the helpful guys on #haskell-lens
channel) to implement the concatMap
-like functionality. 在
concatReplace
我能够使用>>=
(感谢#haskell-lens
频道上有用的人)来实现concatMap
的功能。 But in my real code the function I have is String -> IO [String]
, which would look something like this 但在我的真实代码中,我所拥有的函数是
String -> IO [String]
,它看起来像这样
correctConcatIo :: IO [Foo]
correctConcatIo = (traverse._Foo.traverse._Qux._2) (>>= (return . const ["he", "he"])) items
But this example doesn't typecheck anymore. 但是这个例子不再进行类型检查了。 What I need is to basically put together the logic from
ioReplace
and concatReplace
in a way that I would be able to apply a function with the type String -> IO [String]
to a data structure containing [String]
. 我需要的是基本上将
ioReplace
和concatReplace
的逻辑放在一起,以便能够将类型为String -> IO [String]
的函数应用于包含[String]
的数据结构。
You can only replace a String with [String] if it's already in a list (consider trying to stick a [Int] back into _Qux._1
), so you have to turn your function into [String]->IO [String]
and replace the whole list, using some approach like you've already demonstrated: 如果字符串已经在列表中,则只能用[String]替换字符串(考虑将[Int]重新
_Qux._1
到_Qux._1
),因此必须将函数转换为[String]->IO [String]
并且使用您已经演示的一些方法替换整个列表:
concatMapM f l = fmap concat (mapM f l)
doIOStuff s = return ['a':s, 'b':s]
concatIO :: IO [Foo]
concatIO = (traverse._Foo.traverse._Qux._2) (concatMapM doIOStuff) items
You can even compose that concatMapM onto the end to get something with a LensLike
type, but it's not flexible enough to use with most of the lens combinators. 您甚至可以将concatMapM组合到最后以获得具有
LensLike
类型的东西,但它不够灵活,不能与大多数镜头组合器一起使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.