javascript - 如何处理 Ramda 中的错误
问题描述
我对 Ramda 和函数式编程非常陌生,并试图用 Ramda 重写脚本,但不确定如何以干净的方式处理 Ramda 的错误。这就是我所拥有的,是否有人对如何使用 Ramda 以功能方式重写它有任何指示?
const targetColumnIndexes = targetColumns.map(h => {
if (header.indexOf(h) == -1) {
throw new Error(`Target Column Name not found in CSV header column: ${h}`)
}
return header.indexOf(h)
})
作为参考,这些是header
和targetColumns
const header = [ 'CurrencyCode', 'Name', 'CountryCode' ]
const targetColumns = [ 'CurrencyCode', 'Name' ]
所以我需要:
- 映射到 targetColumns
- 从标头返回目标列的 indexOf
- 如果索引是,则抛出错误
-1
解决方案
正如 customcommander 所说,这种抛出异常的方式在函数式编程中并不容易,这是有充分理由的:它更难推理。
“你的函数返回什么?”
“一个号码。”
“总是?”
“是的,……好吧,除非它抛出异常。”
“那它返回什么?”
“好吧,它没有。”
“所以它返回一个数字还是什么都不返回?”
“大概吧。”
“嗯。”
函数式编程中最常见的操作之一是组合两个函数。但这只有在一个函数的输出与其后继函数的输入匹配时才有效。如果第一个可能抛出异常,这很困难。
为了解决这个问题,FP 世界使用捕获失败概念的类型。您可能已经看到有关Maybe
类型的讨论,它处理的值可能是null
. 另一个常见的类型是Either
(sometimes Result
),它有两个子类型,用于错误情况和成功情况(分别为for orLeft
和for 。)在这些类型中,找到的第一个错误被捕获并传递给需要它的人,而成功案例还在继续处理中。(还有一些类型可以捕获错误列表。)Right
Either
Error
Ok
Result
Validation
这些类型有很多实现。有关一些建议,请参阅幻想世界列表。
Ramda 曾经拥有自己的一组这些类型,但已放弃维护它。Folktale和Sanctuary是我们经常推荐的。但即使是 Ramda 的旧实现也应该这样做。这个版本使用Folktale 的data.either
,因为它是我更了解的版本,但后来的 Folktale 版本将其替换为Result
.
下面的代码块展示了我如何使用Either
s 来处理这种失败的概念,尤其是我们如何R.sequence
将一个数组转换Eithers
为一个Either
持有数组。如果输入包含任何Left
s,则输出只是 a Left
。如果都是Right
s,那么输出是一个Right
包含它们值的数组。有了这个,我们可以将所有列名转换为Either
捕获值或错误的 s,然后将它们组合成一个结果。
需要注意的是,这里没有抛出异常。我们的函数将正确组合。失败的概念被封装在类型中。
const header = [ 'CurrencyCode', 'Name', 'CountryCode' ]
const getIndices = (header) => (targetColumns) =>
map((h, idx = header.indexOf(h)) => idx > -1
? Right(idx)
: Left(`Target Column Name not found in CSV header column: ${h}`)
)(targetColumns)
const getTargetIndices = getIndices(header)
// ----------
const goodIndices = getTargetIndices(['CurrencyCode', 'Name'])
console.log('============================================')
console.log(map(i => i.toString(), goodIndices)) //~> [Right(0), Right(1)]
console.log(map(i => i.isLeft, goodIndices)) //~> [false, false]
console.log(map(i => i.isRight, goodIndices)) //~> [true, true]
console.log(map(i => i.value, goodIndices)) //~> [0, 1]
console.log('--------------------------------------------')
const allGoods = sequence(of, goodIndices)
console.log(allGoods.toString()) //~> Right([0, 1])
console.log(allGoods.isLeft) //~> false
console.log(allGoods.isRight) //~> true
console.log(allGoods.value) //~> [0, 1]
console.log('============================================')
//----------
const badIndices = getTargetIndices(['CurrencyCode', 'Name', 'FooBar'])
console.log('============================================')
console.log(map(i => i.toString(), badIndices)) //~> [Right(0), Right(1), Left('Target Column Name not found in CSV header column: FooBar')
console.log(map(i => i.isLeft, badIndices)) //~> [false, false, true]
console.log(map(i => i.isRight, badIndices)) //~> [true, true, false]
console.log(map(i => i.value, badIndices)) //~> [0, 1, 'Target Column Name not found in CSV header column: FooBar']
console.log('--------------------------------------------')
const allBads = sequence(of, badIndices)
console.log(allBads.toString()) //~> Left('Target Column Name not found in CSV header column: FooBar')
console.log(allBads.isLeft) //~> true
console.log(allBads.isRight) //~> false
console.log(allBads.value) //~> 'Target Column Name not found in CSV header column: FooBar'
console.log('============================================')
.as-console-wrapper {height: 100% !important}
<script src="//bundle.run/ramda@0.26.1"></script>
<!--script src="//bundle.run/ramda-fantasy@0.8.0"></script-->
<script src="//bundle.run/data.either@1.5.2"></script>
<script>
const {map, includes, sequence} = ramda
const Either = data_either;
const {Left, Right, of} = Either
</script>
对我来说主要的一点是,诸如goodIndices
和之类的值badIndices
本身就很有用。如果我们想对它们进行更多处理,我们可以简单地map
对它们进行处理。请注意,例如
map(n => n * n, Right(5)) //=> Right(25)
map(n => n * n, Left('oops')) //=> Left('oops'))
因此,我们的错误不会受到影响,我们的成功会得到进一步处理。
map(map(n => n + 1), badIndices)
//=> [Right(1), Right(2), Left('Target Column Name not found in CSV header column: FooBar')]
这就是这些类型的全部内容。
推荐阅读
- lisp - Common Lisp:函数类型说明符中 * 的问题
- jhipster - 如何在开发模式下本地启动 jhipster 微服务?
- swift - SwiftNIO websocket 服务器和作为客户端的浏览器之间的连接问题
- python - 如何在 python 中编写 N 维符号矩阵?
- python - 如何滚动查看网站上的图像?
- java - 巨大的 hprof 文件解析“Java 堆空间”
- google-kubernetes-engine - 在安装了 jdk 而不是 jre 的谷歌 kubernetes 引擎上部署 spring-boot 应用程序
- python - 使用 python 从现有的 sql 查询中提取数据库模式
- django - /success 处的 ValueError django EmailMultiAlternatives 中的地址无效
- survey - 我可以在surveymonkey 中回答两次相同的调查吗?