haskell - 如何使用 Lenses 来表示 `mapM` 和 `concat` 以连接 IO 操作的结果?

标签 haskell haskell-lens lenses

我正在想办法结合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 channel 上的乐于助人的人)实现 concatMap类似的功能。但在我的真实代码中,我拥有的功能是 String -> IO [String] ,看起来像这样

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

但是这个例子不再进行类型检查。我需要的基本上是将 ioReplace 中的逻辑组合在一起和concatReplace以某种方式,我将能够应用类型 String -> IO [String] 的函数到包含 [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 类型的内容,但它不够灵活,无法与大多数镜头组合器一起使用。

关于haskell - 如何使用 Lenses 来表示 `mapM` 和 `concat` 以连接 IO 操作的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24519389/

相关文章:

c - 使用 hsc2hs 时,在 haskell 源代码中引入 #include 指令会导致大量错误

haskell - 无点镜头创建不进行类型检查

haskell - zipper : mapping over last breadcrumb

haskell - 如何修复这种嵌套 fmap 仿函数的困惑?

Haskell - 也许算术

scala - 使用另一个属性的值修改带有 Monocle 镜头的属性

haskell - 如何在镜头中进行功能类型检查

haskell - 通过 ReaderT 获取带有镜头的元组子集

haskell - 在 Haskell 中,守卫或匹配器更可取吗?

haskell - 类型系列无法返回 RankN 类型——解决方法还是替代方案?