2

在对一堆类型为 Either[String, A] (其中 A 不止一种类型)的 Eithers 进行匹配之后,我想将左侧的任何字符串累积到一个列表中。

(a, b, c, d, e) match {
  case (Right(a), Right(b), Right(c), Right(d), Right(e)) => {
    "All good, use a, b, c, d, and e!"
  }
  case anythingElse => {
    val strings = accLefts(anythingElse)
    doSomethingWithStrings(strings)
  }
}

如果我尝试.productIterator.toList使用元组,我最终会得到 List[Any]。如果我分别处理每个失败的案例(右和左的组合),我最终会得到成倍数量的案例陈述。

如何在最后获得 List[Either[String, Any]] 以传递给我的 accLefts 调用?或者我应该做一些除了比赛之外的事情吗?

4

3 回答 3

5

正是Scalaz(本质上是增强版ValidationNEL)旨在支持的那种东西。例如,假设我们使用 Scalaz 7 进行以下设置:Either

import scalaz._, Scalaz._

case class Person(first: String, last: String, initial: Char, age: Int)

val first = "John".successNel[String]

val last = "Doe".successNel[String]
val badLast = "Empty last name".failureNel[String]

val initial = 'H'.successNel[String]
val badInitial = "Non-alphabetic MI".failureNel[Char]

val age = 45.successNel[String]
val badAge = "Negative age provided".failureNel[Int]

请注意,Nel此处代表非空列表,它 "John".successNel[String]或多或少等同于Right("John"): Either[List[String], String]等。

现在我们可以编写以下内容:

scala> println((first |@| last |@| initial |@| age)(Person.apply))
Success(Person(John,Doe,H,45))

或者:

scala> println((first |@| badLast |@| initial |@| badAge)(Person.apply))
Failure(NonEmptyList(Empty last name, Negative age provided))

或者:

scala> println((first |@| badLast |@| badInitial |@| badAge)(Person.apply))
Failure(NonEmptyList(Empty last name, Non-alphabetic MI, Negative age provided))

任何错误都会累积在ValidationNEL. 有关更多详细信息,请参见例如我的答案。

于 2013-03-01T21:14:39.657 回答
3

也许使用嵌套模式匹配?

case anythingElse => {
    val strings = anythingElse
                    .productIterator
                    .collect { case Left(str: String) => str }
                    .toList
    doSomethingWithStrings(strings)
}

请注意,str: String这里是为了指导类型推断,因此字符串的类型List[String]不是List[Any]

于 2013-03-01T20:22:08.863 回答
0

我可能会创建一组实用功能,例如

def fromTuple2[A, That](t: Tuple2[A,A])(implicit bf : CanBuildFrom[Nothing, A, That]): That =
  (bf.apply() += (t._1, t._2)).result();

对于您需要的所有n元组。虽然这是很多样板代码,但它只是一次性工作。然后您可以执行以下操作:

val e1: Either[String,Int] = Right(3);
val e2: Either[String,String] = Left("3");
val test: List[Either[String,Any]] = fromTuple2(e1, e2);

也许更好,我们可以使用丰富的隐式方法,例如

implicit def fromTuple2Impl[A](t: Tuple2[A,A]) = new {
  def asCollection[That](implicit bf : CanBuildFrom[Nothing, A, That]): That =
    (bf.apply() += (t._1, t._2)).result();
}

只写

val test: List[Either[String,Any]] = (e1, e2).asCollection;

编辑:我们甚至可以将元组丰富为Traversables,这为我们提供了所有方法,如toList、折叠等:

implicit def fromTuple2Impl3[A](t: Tuple2[A,A]) = new Traversable[A] {
  def asCollection[That](implicit bf : CanBuildFrom[Nothing, A, That]): That =
    (bf.apply() += (t._1, t._2)).result();
  override def foreach[U](f: (A) => U): Unit = {
    f(t._1); f(t._2);
  }
}

通过更多的工作,我们可以进一步实施IndexedSeq

于 2013-03-01T21:20:03.803 回答