如何在 Kotlin/Arrow.kt 中为生产者通道实现“铁路模式”

Hec*_*tor 4 android kotlin arrow-kt

我正在研究我当前的 Android 应用程序中的 Kotlin 协程和通道。

我有以下代码来管理远程 Api 调用并控制 UI 副作用

   private val historical: CompletableDeferred<List<Any>> = CompletableDeferred()
   private val mutex = Mutex()

    @ExperimentalCoroutinesApi
    fun perform(action: Action): ReceiveChannel<List<Any>> =
        produce {

          mutex.withLock {
            if (historical.isCompleted) {
                send(historical.getCompleted())
                return@produce
            }  

            send(action.sideEffects)
            val networkResponse = repository.perform(action)
            send(networkResponse.sideEffects)
            send(listOf(networkResponse)).also {
                    historical.complete(listOf(response))
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

上面的代码给了我想要的结果,但是我想将其重构为类似于函数式编程“铁路模式”的东西https://android.jlelse.eu/real-world-function-programming-with-kotlin-arrow- b5a98e72f5e3

我的流程在哪里

stepOne(Historical.completed)
.stepTwo(action.sideEffects)
.stepThree(getReaction())
.stepFour(reaction.sideEffects)
.finalStep(reaction)
Run Code Online (Sandbox Code Playgroud)

这将在任何步骤失败或历史“完成”时“短路”

Kotlin 中可以实现这种调用方式吗?和/或 Kotlin & Arrow.kt?

log*_*cat 5

您可以使用 Arrow-kt 的Either

您可以使用Either's mapLeft()map()flatMap()

如果结果是Exception使用mapLeft(). 返回值将mapLeft()是新的Left结果Either,例如返回是String,结果将是Either<String, List<Any>>。如果结果是Right,即List<Any>mapLeft()将被跳过,但结果类型无论如何都会改变,所以你将拥有类型Either<String, List<Any>>和 值Right<List<Any>>。如果您选择的话,您也可以返回相同的Exception内容mapLeft()

如果您不需要处理特定错误,您可以直接链接map()and flatMap()map()基本上是mapRight(),flatMap()当你想要链接调用时很有用,即在链中的某个地方有一个接收器可能会失败,并且你想以相同的方式List<Any>处理该调用,你可以从返回新的ExceptionEitherEitherflatMap()

代码看起来像这样

fun perform(action: Either<Exception, Action>): ReceiveChannel<List<Any>> =
    produce {
        // if action is always right, you can start it as Right(action) but then first mapLeft does not make any sense
        if (historical.completed) action
            .mapLeft {
                // handle actions exception here
                // transform it to something else or just return it
                send(action.sideEffects)
                it
            }.flatMap {
                // handle right side of action either
                // assume here that repository may fail and returns Either<Exception, NetworkResponse>
                repository.perform(it)
            }.mapLeft {
                // handle repositorys exception here
                // transform it to something else or just return it
                send(it)
                it
            }.map {
                // handle network response
                send(listOf(networkResponse))
                historical.complete(listOf(networkResponse))
            }
    }
Run Code Online (Sandbox Code Playgroud)