我是函数式编程和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) }
}
起初我原本期望使用return
和apply
,因为两个方法调用perhapsExtractLocalDate
是独立的,因此我会使用一种应用风格。相反,我无法弄清楚如何避免使用bind
,这是我的理解意味着一种单一的风格。
我的问题是:
如果我的理解是正确的,我该如何将其转化为适用的风格?
上述实施中是否存在重大错误? (即成语,滥用干扰)
答案 0 :(得分:1)
我相信我明白了什么是错的。
在用适当的函数式编程语言编写的FP示例中,应用样式写成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
函数。