0

Suppose I have a function to check a string:

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

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

Now I would like to reuse it and write a new function to check the head of a list of strings and return either an error or the list tail.

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

This function works but doesn't look elegant. How would you improve it ?

Michael
  • 41,026
  • 70
  • 193
  • 341
  • I don't see how your implementation checks a list of strings - seems like only the head is validated - is this missing a recursive call? – Tzach Zohar Apr 06 '17 at 14:46
  • the function you have will either return an error (associated with the first item in the list) or the tail of the list. Is that actually the behaviour you are looking for? – Angelo Genovese Apr 06 '17 at 14:47
  • 1
    `validateList` works as expected. I should re-phrase the function description. @TzachZohar is correct; the function validates _the head of the list_ rather than the list. – Michael Apr 06 '17 at 15:28

2 Answers2

2

Your implementation looks rather idiomatic to me, but if you're looking for a possibly more concise alternative - I think this does exactly the same, and is (arguably) more concise:

def validateList(xs: List[String]): Either[MyError, List[String]] =
  xs.headOption.map(validate).map(_.right.map(_ => xs.tail)).getOrElse(Left(oops))
Tzach Zohar
  • 37,442
  • 3
  • 79
  • 85
1

Assuming you actually want to return either a single error or the original list, then the following will work. I can't comment on whether this is idiomatic Scala though.

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

For example:

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))
SimonC
  • 6,590
  • 1
  • 23
  • 40
  • Thanks but I want to return either an error or _the tail_ of the list. I am updating the question. – Michael Apr 06 '17 at 15:31