I am learning Scala and today came across the Fail Slow
mechanism using Scalaz ValidationNel
however it is really difficult to understand how to use it. I am reading these blogs: Blog1 , I am reading this StackOverflow post too: StackOverflow but it is really difficult to understand for non functional programmer. Can somebody provide a simple example on how to accumulate errors in ValidationNel
in Scala? It will be really helpful to have a description about the example too.

- 1,491
- 4
- 26
- 67
2 Answers
Using the example from the blog you've linked
val sumV: ValidationNEL[String, Int] = for {
a <- 42.successNel[String]
b <- "Boo".failNel[Int]
c <- "Wah wah".failNel[Int] // by defn of flatMap, can't get here
} yield a + b + c
What this is doing is using a flatMap to chain together various operations. 42.successNel[String], for example, creates a Success, and "Boo".failNel[Int] creates a failure. The way flatMap works here is to continue on to the next operations only on a success. So this is a "fail fast" operation - it gathers the first failure into your error case and stops.
If you want to "fail slow" - ie. gather all possible failures, you need to use a different method. This is where Applicative comes in.
val yes = 3.14.successNel[String]
val doh = "Error".failNel[Double]
def addTwo(x: Double, y: Double) = x + y
(yes |@| yes)(addTwo) // Success(6.28)
(doh |@| doh)(addTwo) // Failure(NonEmptyList(Error, Error))
(a |@| b)(someFunctionOfTwoArgsHere)
- What this is saying is "Perform the 'a' operation, and perform the 'b' operation, and if both are successful, perform someFunctionOfTwoArgsHere(a,b). Otherwise, take any failures and combine them. So if a fails, but b succeeds, you get a Validation failure with the result of a failing. If a AND b fails, you get a Validation failure with the results of both a and b failing.

- 3,395
- 2
- 27
- 46
-
thanks for your answer, what exactly the statement `val yes = 3.14.successNel[String]` means? I am from a OOPs background and I am trying to corelated your example with `if(condition)` statement. – Explorer Oct 04 '17 at 02:17
-
`val yes = 3.14.successNel[String]` is creating an instance of a ValidationNEL[String, Int] object. The type parameters (String and Int) refer to the failure and success cases respectively. This kind of object either contains a non empty list of the type on the lift (String, in this case) or a single instance of a the type on the right (Int). This type of object has the `|@|` behaviour encoded into it, and that behaviour is - take another ValidationNel, and check what the actual value of the objects are - are they both Successes? – Ren Oct 04 '17 at 02:31
-
In that case, take the right arguments and run it through the function, and put the result in a Success object. Or are they both Failure, or one of them is a Failure? In that case, take the Failures and combine them in a list, and put that list in a Failure object, – Ren Oct 04 '17 at 02:33
Previous answer is good but I understand you come from OOP paradigm so let me put another example comparing both paradigms.
Common code:
val a = "1"
val b = "aaa"
val c = "bbb"
def isAllDigits(x: String) = x forall Character.isDigit
def appendError(x: String, errors: mutable.Buffer[String]) = errors += s"$x is not number"
type Errors = NonEmptyList[String]
// disjunction \/ for fail fast
def toDigitFailFast(x: String): Errors \/ Int = {
if (isAllDigits(x)) {
x.toInt.right
} else {
s"$x is not number".wrapNel.left
}
}
// validation nel (non empty list) for fail slow
def toDigitFS(x: String): ValidationNel[String, Int] = {
if (x forall Character.isDigit) {
x.toInt.successNel
} else {
s"$x is not number".failureNel
}
}
Code for fail fast imperative:
// fail fast imperative programming
println("---\nFail Fast imperative")
val failFastErrors = mutable.Buffer.empty[String]
if(isAllDigits(a)) {
if(isAllDigits(b)) {
if(isAllDigits(c)) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
appendError(c, failFastErrors)
}
} else {
appendError(b, failFastErrors)
}
} else {
appendError(a, failFastErrors)
}
if(failFastErrors.nonEmpty) {
println("Errors:")
for(error <- failFastErrors) {
println(error)
}
}
Code for fail fast functional (with disjunction /):
val resultFunc = for {
x <- toDigitFailFast(a)
y <- toDigitFailFast(b)
z <- toDigitFailFast(c)
} yield (x + y + z)
resultFunc match {
case \/-(total) => println(s"Total = $total")
case -\/(errors) =>
println("Errors:")
errors.foreach(println)
}
Output on fail fast (only tells you first error):
Fail Fast imperative
Errors:
aaa is not number
Fail Fast functional
Errors:
aaa is not number
Now the fail slow code for imperative:
// fail slow imperative programming
println("---\nFail Slow imperative")
val failSlowErrors = mutable.Buffer.empty[String]
if(!isAllDigits(a)) {
appendError(a, failSlowErrors)
}
if(!isAllDigits(b)) {
appendError(b, failSlowErrors)
}
if(!isAllDigits(c)) {
appendError(c, failSlowErrors)
}
if(failSlowErrors.isEmpty) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
println("Errors:")
for(error <- failSlowErrors) {
println(error)
}
}
And the functional version (fail slow):
// fail slow functional programming
println("---\nFail Slow functional")
val resultFuncSlow =
(toDigitFS(a) |@| toDigitFS(b) |@| toDigitFS(c)) { _ + _ + _ }
resultFuncSlow match {
case Success(result) => println(result)
case Failure(errors) =>
println("Errors:")
errors.foreach(println)
}
And the output with both errors:
Fail Slow imperative
Errors:
aaa is not number
bbb is not number
---
Fail Slow functional
Errors:
aaa is not number
bbb is not number

- 3,037
- 22
- 20