如何在Scala中以惯用方式编写它?

时间:2017-04-06 14:30:15

标签: scala list either

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

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

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

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

// 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)
  }

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

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

假设您确实想要返回单个错误或原始列表,那么以下内容将起作用。我不能评论这是否是惯用的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))