【问题标题】:Kotlin Either monad: Refactoring a constructor call to handle CPSKotlin Either monad:重构构造函数调用以处理 CPS
【发布时间】:2018-05-08 22:30:20
【问题描述】:

我是函数式编程和 Kotlin 的绝对初学者,试图解决我根据自己提出的问题创建的练习;我目前的问题是“如何使用端口和适配器架构将函数式编程应用到现实世界的应用程序中?”

目前正在学习Either monad,我有以下函数,其中Perhaps<T> 只是一个重命名的Either<Err, T>,用于exception handling

这个函数接受一个包含任意HTTP参数的RequestModel,并且可能Perhaps返回一个CountBetweenQuery,它只是一个包含两个LocalDate的数据类。

private fun requestCountBetweenQueryA(model: RequestModel): Perhaps<CountBetweenQuery> {
    return try {
        Perhaps.ret(CountBetweenQuery(extractLocalDateOrThrow(model, "begin"), extractLocalDateOrThrow(model, "end")))

    } catch (e: UnsupportedTemporalTypeException) {
        Perhaps.Fail(Err.DATE_FORMAT_IS_INVALID)

    } catch (e: DateTimeException) {
        Perhaps.Fail(Err.DATE_FORMAT_IS_INVALID)
    }
}

private fun extractLocalDateOrThrow(it: RequestModel, param: String): LocalDate =
        LocalDate.from(DateTimeFormatter.ISO_DATE.parse(it.parameters.first { it.key == param }.value))

在 OO 语言中,我将对其进行重构,以便在通用异常处理程序中以任何方式处理异常,或在更高的方式处理异常(其中重复的代码被提取到单个方法中)。当然,我想将我的extractLocalDateOrThrow 变成perhapsExtractLocalDate 作为我练习的一部分:

private fun perhapsExtractLocalDate(it: RequestModel, param: String): Perhaps<LocalDate> = try {
    Perhaps.ret(LocalDate.from(DateTimeFormatter.ISO_DATE.parse(it.parameters.first { it.key == param }.value)))

} catch (e: UnsupportedTemporalTypeException) {
    Perhaps.Fail(Err.DATE_FORMAT_IS_INVALID)

} catch (e: DateTimeException) {
    Perhaps.Fail(Err.DATE_FORMAT_IS_INVALID)
}

我已经挣扎了一个小时,试图弄清楚如何调用CountBetweenQuery 的构造函数,同时保留延续传递样式。

这是我想出的:

private fun requestCountBetweenQueryB(me: RequestModel): Perhaps<CountBetweenQuery> {
    val newCountBetweenQueryCurried: (begin: LocalDate) -> (end: LocalDate) -> CountBetweenQuery =
            ::CountBetweenQuery.curried()

    return Perhaps.ret(newCountBetweenQueryCurried)
            .bind { function -> perhapsExtractLocalDate(me, "begin").map(function) }
            .bind { function -> perhapsExtractLocalDate(me, "end").map(function) }
}

一开始我希望使用returnapply 因为这两个方法调用perhapsExtractLocalDate 是独立的,所以我会使用applicative 样式。相反,我无法弄清楚如何避免使用bind,据我了解,这意味着一种单子风格。

我的问题是:

  • 如果我的理解是正确的,我怎么能把它变成applicative style?

  • 在上述实现中是否存在严重错误? (即成语、柯里化的误用)

【问题讨论】:

    标签: functional-programming kotlin continuation-passing


    【解决方案1】:

    我相信我明白出了什么问题。

    在用适当的函数式编程语言编写的 FP 示例中,applicative 样式写成 someFunction map a apply b 但在 Kotlin 中,因为我们处理的是对象的方法,所以这是从左到右读取时以保留顺序编写的,但是在论据评估方面以正确的顺序。这让我很困惑。

    private fun requestCountBetweenQueryC(me: RequestModel): Perhaps<CountBetweenQuery> {
        val newCountBetweenQueryCurried: (begin: LocalDate) -> (end: LocalDate) -> CountBetweenQuery =
                ::CountBetweenQuery.curried()
    
        val a = perhapsExtractLocalDate(me, "begin")
        val b = perhapsExtractLocalDate(me, "end")
    
        return b.apply(a.map(newCountBetweenQueryCurried))
    }
    

    如果我的理解是正确的,这也称为lift2函数。

    【讨论】:

      猜你喜欢
      • 2019-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-25
      相关资源
      最近更新 更多