4

我试图找出一种方法如何以允许以下方式traverseOf结合。>>=

TLDR;简单的 Haskell 中的一个简单示例是这样的,但在数据结构的深处使用镜头。

λ> fmap concat $ mapM ((return :: a -> IO a) . const ["he", "he"]) ["foo", "bar", "baz"]
["he","he","he","he","he","he"]

这是一个带有示例的冗长解释

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

现在最后一个例子是问题所在,因为我通过改变正在应用的函数来作弊。在concatReplace我能够使用>>=(感谢#haskell-lens频道上的乐于助人的人)来实现concatMap类似的功能。但在我的真实代码中,我拥有的功能是String -> IO [String],看起来像这样

correctConcatIo :: IO [Foo]
correctConcatIo = (traverse._Foo.traverse._Qux._2) (>>= (return . const ["he", "he"])) items

但是这个例子不再进行类型检查。我需要的是基本上将逻辑放在一起,ioReplaceconcatReplace以一种我能够将具有该类型的函数应用String -> IO [String]到包含[String].

4

1 回答 1

5

如果 String 已经在列表中,则只能用 [String] 替换它(考虑尝试将 [Int] 重新插入_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

您甚至可以将 concatMapM 组合到最后以获得具有LensLike类型的东西,但它不够灵活,无法与大多数镜头组合器一起使用。

于 2014-07-01T21:37:33.870 回答