Kotlin monad:重构一个构造函数来处理CPS

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

目前正在学习有关monad,我有下面的函数,其中Perhaps只是一个重命名的Either用于exception处理 。

这个函数接受一个包含任意HTTP参数的RequestModelPerhaps返回一个CountBetweenQuery ,它只是一个包含两个LocalDate的数据类。

 private fun requestCountBetweenQueryA(model: RequestModel): Perhaps { 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语言中,我会重构这个,以便在常规exception处理程序中的exception处理方式或上面的更高版本(将重复的代码提取到单个方法中)进行exception处理。 当然,我想把我的extractLocalDateOrThrow变成一个perhapsExtractLocalDate作为我的练习的一部分:

 private fun perhapsExtractLocalDate(it: RequestModel, param: String): Perhaps = 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 { 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是独立的,所以我会用一个应用的风格。 相反,我无法弄清楚如何避免使用bind ,这是从我的理解意味着一个monadic风格。

我的问题是:

  • 如果我的理解是正确的,我怎么能把它变成应用的风格呢?

  • 在上面的实现中是否有严重的错误? (即成语,滥用咖喱)

我相信我明白了什么是错的。

在用适当的函数式编程语言编写的FP例子中,应用风格被写成类似于someFunction map a apply b但是在Kotlin中,因为我们正在处理对象的方法,所以在从左到右读取时这是按保留顺序写的,论证评估方面的正确顺序。 这让我非常困惑。

 private fun requestCountBetweenQueryC(me: RequestModel): Perhaps { 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函数。