Kotlin monad:重构一个构造函数来处理CPS
我是函数式编程和Kotlin的绝对初学者,试图解决我从我自问的问题中创建的练习; 我目前的问题是“如何使用端口和适配器架构将function编程应用于真实世界的应用程序?
目前正在学习有关monad,我有下面的函数,其中Perhaps
只是一个重命名的Either
用于exception处理 。
这个函数接受一个包含任意HTTP参数的RequestModel
, Perhaps
返回一个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) } }
起初我曾预期使用return
和apply
因为两个方法调用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
函数。