如何在 Scala 中地道地编写它?

How to write it idiomatically in Scala?

假设我有一个检查字符串的函数:

case class MyError(msg: String)
val oops = MyError("oops")

def validate(s: String):Either[MyError, Unit] = 
  if (s == "a") Right(()) else Left(oops)

现在我想重用它并编写一个新函数来检查字符串列表的头部和 return 错误或列表尾部。

// should be validateHeadOfList but I don't want to change the name now 

def validateList(xs: List[String]): Either[MyError, List[String]] = 
  xs match { 
    case head::tail => 
      validate(head).fold(err => Left(err), _ => Right(tail))
    case _ => Left(oops)
  }

这个功能有效,但看起来不够优雅。你会如何改进它?

假设您确实想要 return 单个错误或原始列表,那么以下方法将起作用。不过,我无法评论这是否是惯用的 Scala。

case class MyError(msg: String)
val oops = MyError("oops")

def validate(s: String): Either[MyError, Unit] =
  if (s == "a") ().asRight else oops.asLeft

final def validateList(list: List[String]): Either[MyError, List[String]] = {
  @scala.annotation.tailrec
  def loop(xs: List[String]): Either[MyError, List[String]] = {
    xs match {
      case head :: tail =>
        validate(head) match {
          case Left(error) => error.asLeft
          case _ => loop(tail)
        }
      case _ => list.asRight
    }
  }
  loop(list)
}

例如:

scala> validateList(List("a", "a", "a"))
res0: Either[MyError,List[String]] = Right(List(a, a, a))

scala> validateList(List("a", "b", "a"))
res1: Either[MyError,List[String]] = Left(MyError(oops))

你的实现对我来说看起来相当地道,但如果你正在寻找一个可能更简洁的替代方案 - 我认为这完全相同,并且(可以说)更简洁:

def validateList(xs: List[String]): Either[MyError, List[String]] =
  xs.headOption.map(validate).map(_.right.map(_ => xs.tail)).getOrElse(Left(oops))