Scalaz provides a Validation
class that makes a functional approach to this kind of problem very easy. There's a more detailed example of how to use Validation
in this Stack Overflow answer, but I'll also give a sketch here to show how it might work in your situation. I'll assume the following setup:
case class Wine(name: String, grapes: String, country: String)
case class ValidationError(name: String, msg: String)
Now we can write a couple of validation methods (note that I'm using Scalaz 7):
import scalaz._, Scalaz._
def checkNonempty(v: String, name: String, msg: String) =
if (v.nonEmpty) v.successNel else ValidationError(name, msg).failNel
def checkDuplicate(v: String, name: String, msg: String) =
if (true) v.successNel else ValidationError(name, msg).failNel
Where of course you should add your own duplication checking to the last line. Then we can wrap it all together:
def createWine(name: String, grape: String, country: String) = (
checkNonempty(name, "name", "Name not specified").flatMap(_ =>
checkDuplicate(name, "name",
"There already exists a wine with the name '%s'".format(name)
)
) |@|
checkNonempty(grape, "grape", "Grape not specified") |@|
checkNonempty(country, "country", "Country not specified")
)(Wine.apply)
Now if we write something like this:
val result: ValidationNEL[ValidationError, Wine] = createWine(
"Whatever Estates", "Whatever Grape", "U.S."
)
We'll get a success value:
Success(Wine(Whatever Estates,Whatever Grape,U.S.))
But if we give it invalid input:
val result: ValidationNEL[ValidationError, Wine] = createWine(
"", "Some Grape", ""
)
We'll get a list of the accumulated errors:
Failure(
NonEmptyList(
ValidationError(name,Name not specified),
ValidationError(country,Country not specified)
)
)
You could also roll your own validation logic, of course, but using a library like Scalaz is probably worth the trouble if you're doing much of this kind of thing.