使用协程在 kotlin 中的列表上实现 monad 理解

Chr*_*ris 4 kotlin arrow-kt kotlin-coroutines

我想知道是否有可能在具有 monadic 属性的列表或类列表结构上实现类似于 Kotlin 中 Haskell 的 do-notation 的东西。

以下面的例子为例:

fun <A, B> cartesianProduct(xs: List<A>, ys: List<B>): List<Pair<A, B>> =
  xs.flatMap { x -> ys.flatMap { y -> listOf(x to y) } }
Run Code Online (Sandbox Code Playgroud)

如果我能写一些类似的东西就好了

suspend fun <A, B> cartesianProduct(xs: List<A>, ys: List<B>): List<Pair<A, B>> =
  list { 
    val x = xs.bind()
    val y = xs.bind()
    yield(x to y)
  }
Run Code Online (Sandbox Code Playgroud)

Arrow-Kt 使用协程为nullable、option 和 eval定义了类似的理解。我查看了实现及其效果文档,但我无法将概念转换为列表。这在 kotlin 中甚至可能吗?

Raú*_*nez 6

目前无法为 List、Flow 和其他发出多个值的非确定性数据结构实现 monad comprehension。Kotlin 中延续的当前实现只是单次执行。这意味着延续可以使用单个发出的值恢复程序。多次恢复程序需要使用反射劫持延续堆栈标签,以便在第二次恢复时重放它们的状态。此外,重放一个绑定了多重数据类型的块将重放绑定之前的所有效果,因为该块必须再次发射。

list {
  println("printed 3 times and not cool")
  val a = listOf(1, 2, 3).bind()
  a
}
Run Code Online (Sandbox Code Playgroud)

arrow-continuations库已经包含一个用于重置/移位MultiShot 分隔范围,但它目前是内部的,因为在 Kotlin 暂停或延续提供多重射击能力而不重播当前块之前,它是不安全的。或者,我们需要 real for comprehensions 或类似的结构来强制绑定发生在其他代码之前,这也可以解决块重放问题。

Effect 接口最终委托给这些范围之一来实现。当前版本的Reset.suspendedReset.restricted是单发射击。

  • 嗨,Chris,真正的“for 推导式”,就好像它在像 Scala 这样的 lang 中,或者如果 Kotlin for 循环是表达式而不是语句。作为一种语言构造,它们强制绑定按顺序执行。在 Kotlin 延续模型和具有像 Loom monad 这样的延续的模型中,bind 可以作为函数实现,而不需要编译器经常重写为 flatMap、map 或类似形式的“for”风格理解形式的语言构造。 (2认同)