如何在生产者频道的 Kotlin/Arrow.kt 中实施 "Railway Pattern"

How to Implement "Railway Pattern" in Kotlin/Arrow.kt for producer Channel

我正在研究当前 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))
                }
            }
        }

上面的代码给了我想要的结果,但是我想将它重构为类似的东西 函数式编程 "Railway Pattern" https://android.jlelse.eu/real-world-functional-programming-with-kotlin-arrow-b5a98e72f5e3

我的流程在哪里

stepOne(Historical.completed)
.stepTwo(action.sideEffects)
.stepThree(getReaction())
.stepFour(reaction.sideEffects)
.finalStep(reaction)

这将 "short circuit" 在任何步骤失败时或历史 "isCompleted"

在Kotlin中是否可以实现这种调用方式? and/or 科特林 & Arrow.kt?

您可以使用 Arrow-kt 的 Either

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

如果结果是 Exception 使用 mapLeft()mapLeft() 的返回值在结果 Either 中将是新的 Left,例如 return 是 String,结果将是 Either<String, List<Any>>。如果结果是 Right,即 List<Any>mapLeft() 将被跳过,但结果类型无论如何都会改变,因此您将具有 Either<String, List<Any>> 的类型和 [=26] 的值=].您也可以 return 来自 mapLeft()Exception 如果您选择

如果你不需要处理特定的错误,你可以只链接 map()flatMap()map() 基本上是 mapRight() 并且 flatMap() 在您想要链式调用时很有用,即链中某处有一个 List<Any> 的接收器,它可能会失败并且您想处理 ExceptionEither 相同的调用方式,你可以 return new Either from flatMap()

代码看起来像这样

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))
            }
    }