Kotlin 中的 Reader Monad 和 arrow-kt

Ulr*_*ter 4 monads kotlin reader-monad arrow-kt

我正在开发一个中型 Kotlin 项目,我需要通过纯函数的许多嵌套调用来线程化从文件读取的配置信息。对于 Reader monad 来说,这似乎是一个明显的例子。但是,我还没有弄清楚如何在 Kotlin 中有效地实现 Reader。

我正在使用 Arrow 库 (v1.1.3),但令我惊讶的是,它没有附带 Reader 的实现。使用 Arrow 通过函数调用来线程化配置数据的首选方法是什么?由于 Arrow 已转向使用 Kotlin 的本机挂起系统来理解 monad,我认为这意味着不需要专门的 Reader 实现。该怎么做呢?

nom*_*Rev 6

Arrow 过去曾经有 reader monad,但从那时起我们就不再支持此类包装器,转而支持 Kotlin 惯用模式。

在 Kotlin 中,有多种方法可以解决这个问题,但最有希望的是context receivers.

可以在此处找到具体示例,并在此处找到小视频教程。

然而,这在 Kotlin 中还不稳定,目前仅适用于 JVM。有一种方法可以使用扩展函数来解决相同的问题,但目前需要更多的样板文件。R您可以扩展( )的泛型类型Reader,并限制R您需要的实例。

suspend fun <R> R.getProcessUsers(/* add any arguments as needed */): Either<ProcessingError, List<ProcessedUser>>
  where R : Repo,
        R : Persistence =
  fetchUsers().process()
Run Code Online (Sandbox Code Playgroud)

为了最终调用这个函数,您需要R具体化,您可以通过创建RepoandPersistence和接口来实现这一点,然后您可以使用委托。

class DataModule(
  persistence: Persistence,
  repo: Repo
) : Persistence by persistence, Repo by repo

suspend fun main(): Unit {
  // This is your router { get { } } router definition or
  // your Android launch { } or compose function.

  // Generic top-level function automatically got enabled
  val processedUsers = DataModule(MockPersistence(), MockRepo()).getProcessUsers()
  println(processedUsers)

  // Call the alternative approach
  val processedUsers2 = DataModule2(MockPersistence(), MockRepo()).getProcessUsers2()
  println(processedUsers2)
}
Run Code Online (Sandbox Code Playgroud)

然而仍然可以实现Reader,但它可能应该是ReaderT实现suspend它的一个版本的变体。

编辑:

suspend类似于 Arrow 的 DSL支持的实现Reader可以这样实现:

public class Reader<R, A>(public val reader: suspend (R) -> A) {
  
  public companion object {
    public fun <R> ask(): Reader<R, R> = Reader { it }
  }
  
  public fun <T> local(f: (T) -> R): Reader<T, A> = Reader { r: T -> reader(f(r)) }
}

public interface ReaderEffect<R> {
  public suspend fun <A> Reader<R, A>.bind(): A
}

public fun <R, A> reader(action: suspend ReaderEffect<R>.(R) -> A): Reader<R, A> =
  Reader { r ->
    val effect = object : ReaderEffect<R> {
      override suspend fun <A> Reader<R, A>.bind(): A = reader(r)
    }
    action(effect, r)
  }

public val one: Reader<String, Int> = reader { input -> input.toInt() }
public val sum: Reader<String, Int> = reader { one.bind() + one.bind() }

public suspend fun main(): Unit {
  val res = sum.reader.invoke("1")
  println(res) // 2
}
Run Code Online (Sandbox Code Playgroud)