Scala 如何在这两种情况下累积错误?
假设我有几个case类和函数来测试它们:Scala 如何在这两种情况下累积错误?,scala,functional-programming,either,Scala,Functional Programming,Either,假设我有几个case类和函数来测试它们: case class PersonName(...) case class Address(...) case class Phone(...) def testPersonName(pn: PersonName): Either[String, PersonName] = ... def testAddress(a: Address): Either[String, Address] = ... def testPhone(p: Phone): Ei
case class PersonName(...)
case class Address(...)
case class Phone(...)
def testPersonName(pn: PersonName): Either[String, PersonName] = ...
def testAddress(a: Address): Either[String, Address] = ...
def testPhone(p: Phone): Either[String, Phone] = ...
现在我定义了一个新的case类Person
和一个测试函数,它很快就会失败
案例类人员(姓名:PersonName,地址:address,电话:phone)
def testPerson(person:person):或[String,person]=for{
请注意,您希望隔离测试*
方法并停止使用理解
假设(出于任何原因)scalaz不是您的一个选项……不必添加依赖项就可以完成
与许多scalaz示例不同,这是一个库不会比“常规”scala减少太多详细信息的示例:
def testPerson(person: Person): Either[List[String], Person] = {
val name = testPersonName(person.name)
val addr = testAddress(person.address)
val phone = testPhone(person.phone)
val errors = List(name, addr, phone) collect { case Left(err) => err }
if(errors.isEmpty) Right(person) else Left(errors)
}
正如@TravisBrown告诉你的,因为理解并不会真正与错误累积混在一起。事实上,当你不想进行细粒度的错误控制时,通常会使用它们
一个需要理解的人在发现第一个错误时就会“短路”,而这几乎总是你想要的
您所做的坏事是使用String
对异常进行流控制。您应该始终使用或[Exception,Whatever]
并使用scala.util.control.NoStackTrace
和scala.util.NonFatal
微调日志记录
还有更好的选择,具体来说:
scalaz.EitherT
和scalaz.ValidationNel
更新:(这是不完整的,我不知道您到底想要什么)。您有比匹配更好的选项,例如getOrElse
和recover
def testPerson(person: Person): Person = {
val attempt = Try {
val pn = testPersonName(person.name)
val a = testAddress(person.address)
testPhone(person.phone)
}
attempt match {
case Success(person) => //..
case Failure(exception) => //..
}
}
斯卡拉的<代码> < /COD> -理解(这是对调用<代码> >平面图< /代码>和<代码> map < /代码>的组合)的设计,使您能够以一种方式对一元计算进行排序,以便在后续步骤中访问较早计算的结果。
def parseInt(s: String) = try Right(s.toInt) catch {
case _: Throwable => Left("Not an integer!")
}
def checkNonzero(i: Int) = if (i == 0) Left("Zero!") else Right(i)
def inverse(s: String): Either[String, Double] = for {
i <- parseInt(s).right
v <- checkNonzero(i).right
} yield 1.0 / v
当然,虽然这比必要的更为冗长,并且会丢弃一些信息,并且在整个过程中使用验证将更为简洁。从Scala 2.13
开始,我们可以创建或的列表,以便根据元素的或一侧对元素进行分区
// def testName(pn: Name): Either[String, Name] = ???
// def testAddress(a: Address): Either[String, Address] = ???
// def testPhone(p: Phone): Either[String, Phone] = ???
List(testName(Name("name")), testAddress(Address("address")), testPhone(Phone("phone")))
.partitionMap(identity) match {
case (Nil, List(name: Name, address: Address, phone: Phone)) =>
Right(Person(name, address, phone))
case (left, _) =>
Left(left)
}
// Either[List[String], Person] = Left(List("wrong name", "wrong phone"))
// or
// Either[List[String], Person] = Right(Person(Name("name"), Address("address"), Phone("phone")))
如果左侧为空,则没有元素是left
,因此我们可以用右侧的元素构建Person
否则,我们将返回左
值的左
列表
中间步骤(partitionMap
)的详细信息:
我知道这不是你想听到的,而是错误积累和理解(或任何一种单元排序)的不要混用。例如,如果您在调用testAddress
时使用了pn
,而testPersonName
失败了怎么办?请从中查看ValidationNel
。请参阅。在您的示例中:def tePersonName(pn:PersonName):ValidationNel[String,PersonName]=…
(testPersonName(person.name)|@|testAddress(person.address)|@|testPhone(person.phone))(person)
。你也可以看看。它允许你以你想要的方式组合或:对(person)(testPersonName(person.name)、testAddress(person.address)、testPhone(person.phone))
。免责声明:我没有尝试这个库。同样也没有[例外,无论什么]
基本上等同于Try[Watever]
@dmitry-对于“基本”的给定定义@dmitry不一定,您不能总是使用Try
进行流控制。这两种结构有非常具体的使用场景。@dmitry和@flavian那么,在本例中,我可以使用Try
而不是或吗?@Michael您可以使您的代码更干净一点,但Try
仍然没有任何功能lt in专门帮助聚合错误。这可能会更加困难,因为您会发现自己有一个表示多个错误的异常。:31:error:value validation不是[String,Int]的成员
我应该导入什么?myEither.disjunction.validation.toValidationNel
很好。哦,对了,上的.validation
或都是。所以,是的,通过disjunction或使用验证。Fromor
是7.0中最好的选择。就我所记得的,你应该避免在Sca中捕获Throwable
la.所以parseInt
应该捕捉Exception
:case\uuuxception=>…
@r0estir0bbe确实应该是case NonFatal(e)=>
,但我认为这与这个主题没有太大关系。
import scalaz._, syntax.apply._, syntax.std.either._
def testPerson(person: Person): Either[List[String], Person] = (
testPersonName(person.name).validation.toValidationNel |@|
testAddress(person.address).validation.toValidationNel |@|
testPhone(person.phone).validation.toValidationNel
)(Person).leftMap(_.list).toEither
// def testName(pn: Name): Either[String, Name] = ???
// def testAddress(a: Address): Either[String, Address] = ???
// def testPhone(p: Phone): Either[String, Phone] = ???
List(testName(Name("name")), testAddress(Address("address")), testPhone(Phone("phone")))
.partitionMap(identity) match {
case (Nil, List(name: Name, address: Address, phone: Phone)) =>
Right(Person(name, address, phone))
case (left, _) =>
Left(left)
}
// Either[List[String], Person] = Left(List("wrong name", "wrong phone"))
// or
// Either[List[String], Person] = Right(Person(Name("name"), Address("address"), Phone("phone")))
List(Left("bad name"), Right(Address("addr")), Left("bad phone"))
.partitionMap(identity)
// (List[String], List[Any]) = (List("bad name", "bad phone"), List[Any](Address("addr")))