Arrow 曾经让 reader monad 被淘汰,但从那时起我们就停止支持这种包装器,转而支持 Kotlin 惯用模式。
在 Kotlin 中有几种方法可以解决这个问题,但最有希望的是 context receivers。
具体例子可以看here,小视频教程here。
然而,这在 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()
然后最终调用这个函数,你需要使R具体化,你通过使Repo和Persistence和接口来做到这一点,然后你可以使用委托。
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)
}
但是仍然可以实现Reader,但它可能应该是实现suspend 版本的ReaderT 变体。
编辑:
suspend 的实现支持Reader 与类似于 Arrow 的 DSL 可以这样实现:
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
}