如何从元组中收集Either的Lefts?

时间:2013-03-01 20:18:14

标签: scala either

在一堆具有类型E [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]。如果我单独处理每个失败的案例(权利和左派的组合),我最终会得到一个指数的案例陈述。

如何在那里获得一个列表[[String,Any]],以传递给我的accLefts电话?或者我应该做一些不同于匹配的事情?

3 个答案:

答案 0 :(得分:5)

正好 ScalazValidationNEL(基本上是一个强化的Either)旨在支持的事物。例如,假设我们使用Scalaz 7进行以下设置:

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的左侧。参见例如我的回答here了解更多详情。

答案 1 :(得分:3)

也许嵌套模式匹配?

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

请注意,str: String此处用于指导类型推断,因此字符串的类型List[String]不是List[Any]

答案 2 :(得分: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;

编辑:我们甚至可以将元组丰富为Traversable s,这样就可以获得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