Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I reverse of flow of Option Monad?

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?

like image 853
sanjib Avatar asked Nov 15 '10 12:11

sanjib


3 Answers

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}
like image 128
Don Mackenzie Avatar answered Nov 03 '22 17:11

Don Mackenzie


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
like image 20
oxbow_lakes Avatar answered Nov 03 '22 15:11

oxbow_lakes


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
like image 23
Steve Avatar answered Nov 03 '22 16:11

Steve