使用箭头:如何将 (X)->IO<Y> 类型的转换应用于 Sequence<X> 类型的数据以获得 IO<Sequence<Y>>?
With Arrow: How do I apply a transformation of type (X)->IO<Y> to data of type Sequence<X> to get IO<Sequence<Y>>?
我正在使用 Arrow.kt 学习函数式编程,打算遍历路径层次结构并对每个文件进行哈希处理(并做一些其他事情)。强迫自己尽可能多地使用函数式概念。
假设我已经在代码中定义了一个 data class CustomHash(...)
。下面会提到。
首先,我需要通过遍历路径来构建一系列文件。这是一个 impure/effectful 函数,所以它应该用 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>>
可以通过在其中使用 flatMap
来 IO<Hash>
,但是 Sequence
当然没有IO 能力的扁平化。
Arrow 的文档有很多 "TODO" 部分,并且可以非常快速地跳转到假定大量 intermediate/advanced 函数式编程知识的文档。它对这个问题并没有真正的帮助。
首先您需要将 Sequence
转换为 SequenceK
然后您可以使用 sequence
函数来完成。
import arrow.fx.*
import arrow.core.*
import arrow.fx.extensions.io.applicative.applicative
val sequenceOfIOs: Sequence<IO<Any>> = TODO()
val ioOfSequence: IO<Sequence<Any>> = sequenceOfIOs.k()
.sequence(IO.applicative())
.fix()
我正在使用 Arrow.kt 学习函数式编程,打算遍历路径层次结构并对每个文件进行哈希处理(并做一些其他事情)。强迫自己尽可能多地使用函数式概念。
假设我已经在代码中定义了一个 data class CustomHash(...)
。下面会提到。
首先,我需要通过遍历路径来构建一系列文件。这是一个 impure/effectful 函数,所以它应该用 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>>
可以通过在其中使用 flatMap
来 IO<Hash>
,但是 Sequence
当然没有IO 能力的扁平化。
Arrow 的文档有很多 "TODO" 部分,并且可以非常快速地跳转到假定大量 intermediate/advanced 函数式编程知识的文档。它对这个问题并没有真正的帮助。
首先您需要将 Sequence
转换为 SequenceK
然后您可以使用 sequence
函数来完成。
import arrow.fx.*
import arrow.core.*
import arrow.fx.extensions.io.applicative.applicative
val sequenceOfIOs: Sequence<IO<Any>> = TODO()
val ioOfSequence: IO<Sequence<Any>> = sequenceOfIOs.k()
.sequence(IO.applicative())
.fix()