【问题标题】:How to Implement "Railway Pattern" in Kotlin/Arrow.kt for producer Channel如何在 Kotlin/Arrow.kt 中为生产者频道实现“铁路模式”
【发布时间】:2019-11-22 13:07:22
【问题描述】:

我正在我当前的 Android 应用程序中研究 Kotlin Coroutines & Channels。

我有以下代码来管理远程 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))
                }
            }
        }

上面的代码给了我想要的结果,但是我想将它重构为类似的东西 函数式编程“铁路模式”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)

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

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

【问题讨论】:

  • 不确定您的代码是做什么的......但是可以使用 Kotlin 和 Arrow 实现面向铁路的编程。看看这个:arrow-kt.io/docs/patterns/monad_comprehensions
  • 如果每个都返回一个 observable,你也许可以这样做。请参阅 RxJava 和 RxKotlin
  • 面向铁路的编程概念在 Kotlin 中通过 中缀函数 用于相同类型的输入和输出,但结果不同。您能否提供有关您的代码的更多信息(什么是函数和 I/O),以便我可以更好地帮助您解决方法?
  • 您可以在没有箭头的情况下实现 ROP - gist.github.com/harikrishnan83/3c4359bbb91e57eba7b962dc9300c6cc。即使使用箭头“Either”,代码的可读性也会稍差(FlatMaps 可能会妨碍阅读步骤,就像您在问题中所写的方式一样)。您可能需要在 gist.github.com/harikrishnan83/a16ef8f2e6f9287482adc2b29fbb23f2 之上添加一个中缀

标签: android kotlin arrow-kt


【解决方案1】:

您可以使用 Arrow-kt 的Either

您可以使用Either'smapLeft()map()flatMap()

如果结果是Exception,请使用mapLeft()。来自mapLeft() 的返回值在结果Either 中将是新的Left,例如返回是String,结果将是Either&lt;String, List&lt;Any&gt;&gt;。如果结果是Right,即List&lt;Any&gt;mapLeft() 将被跳过,但结果类型无论如何都会改变,因此您将拥有Either&lt;String, List&lt;Any&gt;&gt; 的类型,其值为Right&lt;List&lt;Any&gt;&gt;。如果您愿意,您也可以从mapLeft() 返回相同的Exception

如果您不需要处理特定错误,您可以链接map()flatMap()map() 基本上是 mapRight() 和 flatMap() 在您想要链接调用时很有用,即在链中的某处有一个 List&lt;Any&gt; 的接收器可能会失败并且您想以与 @987654346 相同的方式处理 Exception @ 对于那个电话,你可以从flatMap()返回新的Either

代码看起来像这样

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

【讨论】:

  • 我想知道为什么任何类型都没有 .onLeft { ... } 方法。当然,做一个 mapLeft 是可行的,但是必须一直返回“它”是很尴尬的。我目前更喜欢 .also { if (it.isLeft()) ... }。或者 .apply { if (isLeft()) ... }
  • @Zordid 我猜这样您就可以将一个接一个的调用链接起来,并在一个地方处理所有这些错误。否则,你什么都可以退货。自定义扩展函数 .onLeft 会做我猜的工作。而 iirc 在实际产品中我们有 .mapLeftReturn 扩展函数,它省略了返回
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 2012-03-01
  • 2016-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多