我正在使用 Arrow.kt 学习函数式编程,打算遍历路径层次结构并散列每个文件(并做一些其他事情)。强迫自己尽可能多地使用功能概念。
假设我data class CustomHash(...)
已经在代码中定义了一个。下面会参考。
首先,我需要通过走路径来构建一系列文件。这是一个不纯/有效的函数,因此应该用IO
monad 将其标记为:
fun getFiles(rootPath: File): IO<Sequence<File>> = IO {
rootPath.walk() // This function is of type (File)->Sequence<File>
}
我需要阅读文件。再次,不纯,所以这被标记为IO
fun getRelevantFileContent(file: File): IO<Array<Byte>> {
// Assume some code here to extract only certain data relevant for my hash
}
然后我有一个计算哈希的函数。如果它需要一个字节数组,那么它是完全纯的。之所以这样做,suspend
是因为执行起来会很慢:
suspend fun computeHash(data: Array<Byte>): CustomHash {
// code to compute the hash
}
我的问题是如何以功能性的方式将所有这些链接在一起。
fun main(rootPath: File) {
val x = getFiles(rootPath) // IO<Sequence<File>>
.map { seq -> // seq is of type Sequence<File>
seq.map { getRelevantFileContent(it) } // This produces Sequence<IO<Hash>>
}
}
}
现在,如果我尝试这个,x
是IO<Sequence<IO<Hash>>>
. 我很清楚为什么会这样。
有什么办法可以Sequence<IO<Any>>
变成IO<Sequence<Any>>
? 我想这本质上可能是术语不精确,采用在它们自己的协程中执行的代码块并在同一个协程上运行所有代码块?
如果 Sequence 不存在,我知道IO<IO<Hash>>
可以在那里IO<Hash>
使用 a flatMap
,但Sequence
当然没有 IO 功能的扁平化。
Arrow 的文档有很多“TODO”部分,并且可以非常快速地跳转到假定大量中级/高级函数式编程知识的文档。它对这个问题并没有真正的帮助。