say, I have a bunch of "validation" functions that return None if there is no error, otherwise it return Some(String) specifying the error message. Something like the following ...
def validate1:Option[String]
def validate2:Option[String]
def validate3:Option[String]
I am going to call them in a sequence and as soon as one returns Some(String), I stop and return the same. If it returns None, I go to the next until the sequence is over. If all of them return None, I return None.
I would like to glue them together in a "for expression". Something like ...
for( a <- validate1; b <- validate2; c <- validate3) yield None;
However, Option flows exactly the opposite what I want here. It stops at None and follows with Some(String).
How can I achieve something like that?
You could just chain the calls together with the orElse method on Option
validate1 orElse validate2 orElse validate3
or you could run a fold over a collection of validate methods converted to functions
val vlist= List(validate1 _, validate2 _, validate3 _)
vlist.foldLeft(None: Option[String]) {(a, b) => if (a == None) b() else a}
The scalaz library has a type called Validation
which allows for some incredible gymnastics with building both errors and success. For example, suppose you have a few methods which can either return a failure message or some successful outcome (A/B/C):
import scalaz._; import Scalaz._
def fooA : ValidationNEL[String, A]
def fooB : ValidationNEL[String, B]
def fooC : ValidationNEL[String, C]
These can be used with the applicative functor to chain the calls together:
(foo1 <|**|> (foo2, foo3)) match {
case Success( (a, b, c) ) => //woot
case Failure(msgs) => //erk
}
Note that if any one of foo1/2/3
fails, then the whole composition fails with a non-empty list (NEL) of failure messages. If more than one fails, you get all failure messages.
It's a killer app. Examples of how tor return a success and failure are as follows
def foo1 : ValidationNEL[String, Int] = 1.success
def foo2 : ValidationNEL[String, Double] = "some error msg".failNel
Can't you just combine the iterators together and then take the first element? Something like:
scala> def validate1: Option[String] = {println("1"); None}
scala> def validate2: Option[String] = {println("2"); Some("error")}
scala> def validate3: Option[String] = {println("3"); None}
scala> (validate1.iterator ++ validate2.iterator ++ validate3.iterator).next
1
2
res5: String = error
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With