Scala 增量更新对象字段
Scala update object fields incrementally
我需要建立包含多个字段的记录。
记录定义如下:
class Record(
f1: Int,
f2: String,
...
err: String
)
逻辑是这样的:
record.f1 = get_f1()
record.f2 = get_f2()
...
对get_f1等的调用可能会抛出异常。发生这种情况时,我们将 record.err 设置为异常消息。
现在,我的 hacky 解决方案是使用可变 Record 实例并逐步修补字段。当异常发生时,我将从外部函数中捕获它并设置错误字段。
问题是如何以更实用的方式实现它?
------编辑-----
我们需要err字段来输出第一个异常
当异常发生时,我们仍然希望输出已经填充的字段
这是一种(主要)实用的方法:
import scala.util.{Try, Success, Failure}
Try {
Record(getf1(), getf2(), "")
} match {
case Success(r) => r
case Failure(e) => Record(0, "", e.getMessage)
}
如果 Try
中的代码运行正常,那么它将 return Success
并且 match
将提取值并 return 它。
如果代码抛出异常,那么它将被 Try
捕获,并且它将 return Failure
。 match
将捕获错误代码,然后创建一个 Return
的值,并将 err
设置为来自异常的错误消息。
正如其他人所提到的,Either
类型是处理错误情况的更好方法,因为它从 Record
class 本身中删除了错误信息:
Try {
Record(getf1(), getf2())
}.toEither
成功时结果为 Right(<record>)
,错误时为 Left(<exception>)
。您可以使用 match
来确定给出的答案。
对于修改后的问题,您需要使用 Try
调用的嵌套链,记录中的每个字段都嵌套一层。
Try(getf1()) match {
case Failure(e) => Record(0, "", e.getMessage)
case Success(f1) =>
Try(getf2()) match {
case Failure(e) => Record(f1, "", e.getMessage)
case Success(f2) => Record(f1, f2, "")
}
}
这个相当曲折的代码是问题中混合错误处理方法的结果。更标准的设计会对整个记录(如上例所示)或每个单独的字段(通过使每个字段成为 Option
或 Either
值)进行错误处理。
The question is how can I implement this in a more functional way?
通过以功能样式正确建模。
首先,让我们从 case class
中删除 err
字段,而是使用 Either
其次,让我们使用 Option
正确处理 nulls
最后,让我们使用像 cats.
这样的函数库
最终结果会是这样的:
import cats.data.EitherNec
import cats.syntax.all._
type Error = String // You may use a better data type.
final case class Record(
f1: Int,
f2: String,
f3: Boolean
)
object Record {
def fromRawData(f1: Option[Int], f2: Option[String], f3: Option[Boolean]): EitherNec[Error, Record] =
(
f1.toRightNec(left = "F1 was empty"),
f2.toRightNec(left = "F2 was empty"),
f3.toRightNec(left = "F3 was empty")
).parMapN(Record.apply)
}
The EitherNec[E, A]
is just an alias for Either[NonEmptyChain[E], A]
Meaning that if it is a Left
it will hold all the errors;
it also implies that there must be at least one error.
如有任何 follow-up 问题,请随时提出。
如果您不想使用 cats,您可以将 parMapN
替换为 for
;但这将在第一次失败时停止。
可以看到代码运行 here.
我需要建立包含多个字段的记录。
记录定义如下:
class Record(
f1: Int,
f2: String,
...
err: String
)
逻辑是这样的:
record.f1 = get_f1()
record.f2 = get_f2()
...
对get_f1等的调用可能会抛出异常。发生这种情况时,我们将 record.err 设置为异常消息。
现在,我的 hacky 解决方案是使用可变 Record 实例并逐步修补字段。当异常发生时,我将从外部函数中捕获它并设置错误字段。
问题是如何以更实用的方式实现它?
------编辑-----
我们需要err字段来输出第一个异常 当异常发生时,我们仍然希望输出已经填充的字段
这是一种(主要)实用的方法:
import scala.util.{Try, Success, Failure}
Try {
Record(getf1(), getf2(), "")
} match {
case Success(r) => r
case Failure(e) => Record(0, "", e.getMessage)
}
如果 Try
中的代码运行正常,那么它将 return Success
并且 match
将提取值并 return 它。
如果代码抛出异常,那么它将被 Try
捕获,并且它将 return Failure
。 match
将捕获错误代码,然后创建一个 Return
的值,并将 err
设置为来自异常的错误消息。
正如其他人所提到的,Either
类型是处理错误情况的更好方法,因为它从 Record
class 本身中删除了错误信息:
Try {
Record(getf1(), getf2())
}.toEither
成功时结果为 Right(<record>)
,错误时为 Left(<exception>)
。您可以使用 match
来确定给出的答案。
对于修改后的问题,您需要使用 Try
调用的嵌套链,记录中的每个字段都嵌套一层。
Try(getf1()) match {
case Failure(e) => Record(0, "", e.getMessage)
case Success(f1) =>
Try(getf2()) match {
case Failure(e) => Record(f1, "", e.getMessage)
case Success(f2) => Record(f1, f2, "")
}
}
这个相当曲折的代码是问题中混合错误处理方法的结果。更标准的设计会对整个记录(如上例所示)或每个单独的字段(通过使每个字段成为 Option
或 Either
值)进行错误处理。
The question is how can I implement this in a more functional way?
通过以功能样式正确建模。
首先,让我们从 case class
中删除 err
字段,而是使用 Either
其次,让我们使用 Option
正确处理 nulls
最后,让我们使用像 cats.
最终结果会是这样的:
import cats.data.EitherNec
import cats.syntax.all._
type Error = String // You may use a better data type.
final case class Record(
f1: Int,
f2: String,
f3: Boolean
)
object Record {
def fromRawData(f1: Option[Int], f2: Option[String], f3: Option[Boolean]): EitherNec[Error, Record] =
(
f1.toRightNec(left = "F1 was empty"),
f2.toRightNec(left = "F2 was empty"),
f3.toRightNec(left = "F3 was empty")
).parMapN(Record.apply)
}
The
EitherNec[E, A]
is just an alias forEither[NonEmptyChain[E], A]
Meaning that if it is aLeft
it will hold all the errors; it also implies that there must be at least one error.
如有任何 follow-up 问题,请随时提出。
如果您不想使用 cats,您可以将 parMapN
替换为 for
;但这将在第一次失败时停止。
可以看到代码运行 here.