This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Accumulating Validation Errors

1 reply
Tim P
Joined: 2011-07-28,
User offline. Last seen 1 year 4 weeks ago.
Hi
I'm trying to use Validation but I want to accumulate all the error strings in my output. The horribly messy code below is the result. First I accumulate my validations, then I check to see if any of them are actual failures and collect the strings together. If any failures I return a fail - but I have to cast it with asInstanceOf. If I succeed, the easiest way of extract the success side is with a for.
It works, but is there a nicer way to do it? I don't (yet) get the totality of the scalaz library and really don't have time today to learn it all. I've got to get something working quickly.
Code is below (addKeyword simply puts the record name onto any error messages that occur for ease of finding the problem).
  def buildLoadRateTerm(node: Node): Validation[NonEmptyList[String], (LoadRateTerm, String)] = {    val name = getName(node)    val keyword = name match {       case Success(name) => "Loadrate " + name + ": "       case _ => "Loadrate with no name: "    }        val loadCategoryName = addKeyword(keyword, getString(node, "loadCategory"))    val crewFactor = addKeyword(keyword, getValue[Double](node, "crewFactor"))    val timePerUnit = addKeyword(keyword, getValue[Duration](node, "timePerUnit"))    val timePerWeight = addKeyword(keyword, getValue[Duration](node, "timePerWeight"))    val timePerVolume = addKeyword(keyword, getValue[Duration](node, "timePerVolume"))
    val allMessages = List(name, loadCategoryName, crewFactor, timePerUnit, timePerWeight, timePerVolume) filter (_.isFailure) map (_ match {        case Failure(msgs) => msgs.list    }) flatten     if (allMessages.size > 0)       (allMessages.fail).asInstanceOf[Validation[NonEmptyList[String], (LoadRateTerm, String)]]    else       for {           a <- name           b <- loadCategoryName           c <- crewFactor           d <- timePerUnit           e <- timePerWeight           f <- timePerVolume        } yield (LoadRateTerm(a, c, d, e, f), b)    }
CheersTim
Robert Wills
Joined: 2009-03-04,
User offline. Last seen 32 weeks 4 days ago.
Re: Accumulating Validation Errors
Hi Tim,
The short answer is that you need liftFailNEL which lifts string validation errors into NonEmptyList[String]
You're also better off using an applicative functor for constructing LoadRateTerm.  
eg see the lines from ExampleValidation.scala in the examples project of the scalaz distribution: case class Person(name: Name, age: Age) def mkPerson(name: String, age: Int) = (Name(name).liftFailNel ⊛ Age(age).liftFailNel){ (n, a) => Person(n, a)}
If the baby stays silent for much longer I'll post up something more detailed.
-Rob

On Sun, Oct 30, 2011 at 11:03 AM, Tim P <tim.pigden@optrak.com> wrote:
Hi
I'm trying to use Validation but I want to accumulate all the error strings in my output.  The horribly messy code below is the result. First I accumulate my validations, then I check to see if any of them are actual failures and collect the strings together. If any failures I return a fail - but I have to cast it with asInstanceOf. If I succeed, the easiest way of extract the success side is with a for.
It works, but is there a nicer way to do it? I don't (yet) get the totality of the scalaz library and really don't have time today to learn it all. I've got to get something working quickly.
Code is below (addKeyword simply puts the record name onto any error messages that occur for ease of finding the problem).
  def buildLoadRateTerm(node: Node): Validation[NonEmptyList[String], (LoadRateTerm, String)] = {     val name = getName(node)    val keyword = name match {       case Success(name) => "Loadrate " + name + ": "        case _ => "Loadrate with no name: "    }        val loadCategoryName = addKeyword(keyword, getString(node, "loadCategory"))     val crewFactor = addKeyword(keyword, getValue[Double](node, "crewFactor"))    val timePerUnit = addKeyword(keyword, getValue[Duration](node, "timePerUnit"))     val timePerWeight = addKeyword(keyword, getValue[Duration](node, "timePerWeight"))    val timePerVolume = addKeyword(keyword, getValue[Duration](node, "timePerVolume"))
    val allMessages = List(name, loadCategoryName, crewFactor, timePerUnit, timePerWeight, timePerVolume) filter (_.isFailure) map (_ match {         case Failure(msgs) => msgs.list    }) flatten     if (allMessages.size > 0)       (allMessages.fail).asInstanceOf[Validation[NonEmptyList[String], (LoadRateTerm, String)]]     else       for {           a <- name            b <- loadCategoryName           c <- crewFactor           d <- timePerUnit            e <- timePerWeight           f <- timePerVolume        } yield (LoadRateTerm(a, c, d, e, f), b)     }
CheersTim

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland