如何处理Ramda中的错误

Le *_*Moi 5 javascript functional-programming ramda.js

我对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)
  })
Run Code Online (Sandbox Code Playgroud)

为了参考,这些是的值headertargetColumns

const header = [ 'CurrencyCode', 'Name', 'CountryCode' ]
const targetColumns = [ 'CurrencyCode', 'Name' ]
Run Code Online (Sandbox Code Playgroud)

所以我需要:

  • 映射到targetColumns
  • 从标头返回indexOf targetColumn
  • 如果索引是 -1

Sco*_*yet 7

正如customcommander所说的,有一个很好的理由是,函数编程不能使这种抛出异常的样式变得容易:这很难推理。

“您的功能返回了什么?”

“一个号码。”

“总是?”

“是的,好吧,除非抛出异常。”

“它返回什么?”

“那不是。”

“那么它返回数字还是什么都不返回?”

“大概吧。”

“嗯。”

函数编程中最常见的操作之一是组成两个函数。但这仅在一个函数的输出与其后继输入匹配时才有效。如果第一个可能引发异常,这将很困难。

为了解决这个问题,FP世界使用捕获失败概念的类型。您可能已经看过有关Maybe类型的讨论,该类型处理的可能是null。另一个常见的错误是Either(有时为Result),对于错误情况和成功情况,它们分别具有两个子类型(分别LeftRightfor EitherErrorand和Okfor)Result。在这些类型中,捕获的第一个错误被捕获并向下传递给任何需要它的人,而成功案例仍在继续处理中。(还有Validation捕获错误列表的类型。)

这些类型有许多实现。有关一些建议,请参见幻想世界列表

Ramda曾经有自己的一组此类类型,但已放弃维护它。我们经常为此推荐民间故事和圣所。但是,即使Ramda的旧实现也应该做到。这个版本使用了Folktale,data.either因为它是我所了解的更好的版本,但是更高版本的Folktale则用代替了它Result

以下代码块显示了如何使用Eithers处理失败的概念,特别是如何使用R.sequence来将数组Eithers转换为Either保持数组。如果输入包含任何Left,则输出仅为Left。如果全部为Rights,则输出为一个Right包含其值的数组。这样,我们可以将所有列名称转换为Eithers,以捕获值或错误,然后将它们组合为单个结果。

需要注意的是,这里没有抛出异常。我们的职能将正确组成。故障的概念封装在类型中。

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('============================================')
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper {height: 100% !important}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

对我来说,主要要点是,诸如goodIndices和的值本身badIndices是有用的。如果我们想对它们进行更多处理,我们可以map对它们进行简单处理。请注意例如

map(n => n * n, Right(5))     //=> Right(25)
map(n => n * n, Left('oops')) //=> Left('oops'))
Run Code Online (Sandbox Code Playgroud)

因此,我们的错误将任其独立,我们的成功将得到进一步处理。

map(map(n => n + 1), badIndices) 
//=> [Right(1), Right(2), Left('Target Column Name not found in CSV header column: FooBar')]
Run Code Online (Sandbox Code Playgroud)

这就是这些类型的全部含义。