I'm trying to validate the parameters of a method for nullity but i don't find the solution...
Can someone tell me how to do?
I'm trying something like this:
def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[Error,Category] = {
val errors: Option[String] = for {
_ <- Option(user).toRight("User is mandatory for a normal category").right
_ <- Option(parent).toRight("Parent category is mandatory for a normal category").right
_ <- Option(name).toRight("Name is mandatory for a normal category").right
errors : Option[String] <- Option(description).toRight("Description is mandatory for a normal category").left.toOption
} yield errors
errors match {
case Some(errorString) => Left( Error(Error.FORBIDDEN,errorString) )
case None => Right( buildTrashCategory(user) )
}
}
If you're willing to use Scalaz, it has a handful of tools that make this kind of task more convenient, including a new Validation class and some useful right-biased type class instances for plain old scala.Either. I'll give an example of each here.
Accumulating errors with Validation
First for our Scalaz imports (note that we have to hide scalaz.Category to avoid the name conflict):
import scalaz.{ Category => _, _ }
import syntax.apply._, syntax.std.option._, syntax.validation._
I'm using Scalaz 7 for this example. You'd need to make some minor changes to use 6.
I'll assume we have this simplified model:
case class User(name: String)
case class Category(user: User, parent: Category, name: String, desc: String)
Next I'll define the following validation method, which you can easily adapt if you move to an approach that doesn't involve checking for null values:
def nonNull[A](a: A, msg: String): ValidationNel[String, A] =
Option(a).toSuccess(msg).toValidationNel
The Nel part stands for "non-empty list", and a ValidationNel[String, A] is essentially the same as an Either[List[String], A].
Now we use this method to check our arguments:
def buildCategory(user: User, parent: Category, name: String, desc: String) = (
nonNull(user, "User is mandatory for a normal category") |#|
nonNull(parent, "Parent category is mandatory for a normal category") |#|
nonNull(name, "Name is mandatory for a normal category") |#|
nonNull(desc, "Description is mandatory for a normal category")
)(Category.apply)
Note that Validation[Whatever, _] isn't a monad (for reasons discussed here, for example), but ValidationNel[String, _] is an applicative functor, and we're using that fact here when we "lift" Category.apply into it. See the appendix below for more information on applicative functors.
Now if we write something like this:
val result: ValidationNel[String, Category] =
buildCategory(User("mary"), null, null, "Some category.")
We'll get a failure with the accumulated errors:
Failure(
NonEmptyList(
Parent category is mandatory for a normal category,
Name is mandatory for a normal category
)
)
If all of the arguments had checked out, we'd have a Success with a Category value instead.
Failing fast with Either
One of the handy things about using applicative functors for validation is the ease with which you can swap out your approach to handling errors. If you want to fail on the first instead of accumulating them, you can essentially just change your nonNull method.
We do need a slightly different set of imports:
import scalaz.{ Category => _, _ }
import syntax.apply._, std.either._
But there's no need to change the case classes above.
Here's our new validation method:
def nonNull[A](a: A, msg: String): Either[String, A] = Option(a).toRight(msg)
Almost identical to the one above, except that we're using Either instead of ValidationNEL, and the default applicative functor instance that Scalaz provides for Either doesn't accumulate errors.
That's all we need to do to get the desired fail-fast behavior—no changes are necessary to our buildCategory method. Now if we write this:
val result: Either[String, Category] =
buildCategory(User("mary"), null, null, "Some category.")
The result will contain only the first error:
Left(Parent category is mandatory for a normal category)
Exactly as we wanted.
Appendix: Quick introduction to applicative functors
Suppose we have a method with a single argument:
def incremented(i: Int): Int = i + 1
And suppose also that we want to apply this method to some x: Option[Int] and get an Option[Int] back. The fact that Option is a functor and therefore provides a map method makes this easy:
val xi = x map incremented
We've "lifted" incremented into the Option functor; that is, we've essentially changed a function mapping Int to Int into one mapping Option[Int] to Option[Int] (although the syntax muddies that up a bit—the "lifting" metaphor is much clearer in a language like Haskell).
Now suppose we want to apply the following add method to x and y in a similar fashion.
def add(i: Int, j: Int): Int = i + j
val x: Option[Int] = users.find(_.name == "John").map(_.age)
val y: Option[Int] = users.find(_.name == "Mary").map(_.age) // Or whatever.
The fact that Option is a functor isn't enough. The fact that it's a monad, however, is, and we can use flatMap to get what we want:
val xy: Option[Int] = x.flatMap(xv => y.map(add(xv, _)))
Or, equivalently:
val xy: Option[Int] = for { xv <- x; yv <- y } yield add(xv, yv)
In a sense, though, the monadness of Option is overkill for this operation. There's a simpler abstraction—called an applicative functor—that's in-between a functor and a monad and that provides all the machinery we need.
Note that it's in-between in a formal sense: every monad is an applicative functor, every applicative functor is a functor, but not every applicative functor is a monad, etc.
Scalaz gives us an applicative functor instance for Option, so we can write the following:
import scalaz._, std.option._, syntax.apply._
val xy = (x |#| y)(add)
The syntax is a little odd, but the concept isn't any more complicated than the functor or monad examples above—we're just lifting add into the applicative functor. If we had a method f with three arguments, we could write the following:
val xyz = (x |#| y |#| z)(f)
And so on.
So why bother with applicative functors at all, when we've got monads? First of all, it's simply not possible to provide monad instances for some of the abstractions we want to work with—Validation is the perfect example.
Second (and relatedly), it's just a solid development practice to use the least powerful abstraction that will get the job done. In principle this may allow optimizations that wouldn't otherwise be possible, but more importantly it makes the code we write more reusable.
I completely support Ben James' suggestion to make a wrapper for the null-producing api. But you'll still have the same problem when writing that wrapper. So here are my suggestions.
Why monads why for comprehension? An overcomplication IMO. Here's how you could do that:
def buildNormalCategory
( user: User, parent: Category, name: String, description: String )
: Either[ Error, Category ]
= Either.cond(
!Seq(user, parent, name, description).contains(null),
buildTrashCategory(user),
Error(Error.FORBIDDEN, "null detected")
)
Or if you insist on having the error message store the name of the parameter, you could do the following, which would require a bit more boilerplate:
def buildNormalCategory
( user: User, parent: Category, name: String, description: String )
: Either[ Error, Category ]
= {
val nullParams
= Seq("user" -> user, "parent" -> parent,
"name" -> name, "description" -> description)
.collect{ case (n, null) => n }
Either.cond(
nullParams.isEmpty,
buildTrashCategory(user),
Error(
Error.FORBIDDEN,
"Null provided for the following parameters: " +
nullParams.mkString(", ")
)
)
}
If you like the applicative functor approach of #Travis Brown's answer, but you don't like the Scalaz syntax or otherwise just don't want to use Scalaz, here is a simple library which enriches the standard library Either class to act as an applicative functor validation: https://github.com/youdevise/eithervalidation
For example:
import com.youdevise.eithervalidation.EitherValidation.Implicits._
def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[List[Error], Category] = {
val validUser = Option(user).toRight(List("User is mandatory for a normal category"))
val validParent = Option(parent).toRight(List("Parent category is mandatory for a normal category"))
val validName = Option(name).toRight(List("Name is mandatory for a normal category"))
Right(Category)(validUser, validParent, validName).
left.map(_.map(errorString => Error(Error.FORBIDDEN, errorString)))
}
In other words, this function will return a Right containing your Category if all of the Eithers were Rights, or it will return a Left containing a List of all the Errors, if one or more were Lefts.
Notice the arguably more Scala-ish and less Haskell-ish syntax, and a smaller library ;)
Lets suppose you have completed Either with the following quick and dirty stuff:
object Validation {
var errors = List[String]()
implicit class Either2[X] (x: Either[String,X]){
def fmap[Y](f: X => Y) = {
errors = List[String]()
//println(s"errors are $errors")
x match {
case Left(s) => {errors = s :: errors ; Left(errors)}
case Right(x) => Right(f(x))
}
}
def fapply[Y](f: Either[List[String],X=>Y]) = {
x match {
case Left(s) => {errors = s :: errors ; Left(errors)}
case Right(v) => {
if (f.isLeft) Left(errors) else Right(f.right.get(v))
}
}
}
}}
consider a validation function returning an Either:
def whenNone (value: Option[String],msg:String): Either[String,String] =
if (value isEmpty) Left(msg) else Right(value.get)
an a curryfied constructor returning a tuple:
val me = ((user:String,parent:String,name:String)=> (user,parent,name)) curried
You can validate it with :
whenNone(None,"bad user")
.fapply(
whenNone(Some("parent"), "bad parent")
.fapply(
whenNone(None,"bad name")
.fmap(me )
))
Not a big deal.
Related
I am trying to use Cats datatype Ior to accumulate both errors and successes of using a service (which can return an error).
def find(key: String): F[Ior[NonEmptyList[Error], A]] = {
(for {
b <- service.findByKey(key)
} yield b.rightIor[NonEmptyList[Error]])
.recover {
case e: Error => Ior.leftNel(AnotherError)
}
}
def findMultiple(keys: List[String]): F[Ior[NonEmptyList[Error], List[A]]] = {
keys map find reduce (_ |+| _)
}
My confusion lies in how to combine the errors/successes. I am trying to use the Semigroup combine (infix syntax) to combine with no success. Is there a better way to do this? Any help would be great.
I'm going to assume that you want both all errors and all successful results. Here's a possible implementation:
class Foo[F[_]: Applicative, A](find: String => F[IorNel[Error, A]]) {
def findMultiple(keys: List[String]): F[IorNel[Error, List[A]]] = {
keys.map(find).sequence.map { nelsList =>
nelsList.map(nel => nel.map(List(_)))
.reduceOption(_ |+| _).getOrElse(Nil.rightIor)
}
}
}
Let's break it down:
We will be trying to "flip" a List[IorNel[Error, A]] into IorNel[Error, List[A]]. However, from doing keys.map(find) we get List[F[IorNel[...]]], so we need to also "flip" it in a similar fashion first. That can be done by using .sequence on the result, and is what forces F[_]: Applicative constraint.
N.B. Applicative[Future] is available whenever there's an implicit ExecutionContext in scope. You can also get rid of F and use Future.sequence directly.
Now, we have F[List[IorNel[Error, A]]], so we want to map the inner part to transform the nelsList we got. You might think that sequence could be used there too, but it can not - it has the "short-circuit on first error" behavior, so we'd lose all successful values. Let's try to use |+| instead.
Ior[X, Y] has a Semigroup instance when both X and Y have one. Since we're using IorNel, X = NonEmptyList[Z], and that is satisfied. For Y = A - your domain type - it might not be available.
But we don't want to combine all results into a single A, we want Y = List[A] (which also always has a semigroup). So, we take every IorNel[Error, A] we have and map A to a singleton List[A]:
nelsList.map(nel => nel.map(List(_)))
This gives us List[IorNel[Error, List[A]], which we can reduce. Unfortunately, since Ior does not have a Monoid, we can't quite use convenient syntax. So, with stdlib collections, one way is to do .reduceOption(_ |+| _).getOrElse(Nil.rightIor).
This can be improved by doing few things:
x.map(f).sequence is equivalent to doing x.traverse(f)
We can demand that keys are non-empty upfront, and give nonempty result back too.
The latter step gives us Reducible instance for a collection, letting us shorten everything by doing reduceMap
class Foo2[F[_]: Applicative, A](find: String => F[IorNel[Error, A]]) {
def findMultiple(keys: NonEmptyList[String]): F[IorNel[Error, NonEmptyList[A]]] = {
keys.traverse(find).map { nelsList =>
nelsList.reduceMap(nel => nel.map(NonEmptyList.one))
}
}
}
Of course, you can make a one-liner out of this:
keys.traverse(find).map(_.reduceMap(_.map(NonEmptyList.one)))
Or, you can do the non-emptiness check inside:
class Foo3[F[_]: Applicative, A](find: String => F[IorNel[Error, A]]) {
def findMultiple(keys: List[String]): F[IorNel[Error, List[A]]] = {
NonEmptyList.fromList(keys)
.map(_.traverse(find).map { _.reduceMap(_.map(List(_))) })
.getOrElse(List.empty[A].rightIor.pure[F])
}
}
Ior is a good choice for warning accumulation, that is errors and a successful value. But, as mentioned by Oleg Pyzhcov, Ior.Left case is short-circuiting. This example illustrates it:
scala> val shortCircuitingErrors = List(
Ior.leftNec("error1"),
Ior.bothNec("warning2", 2),
Ior.bothNec("warning3", 3)
).sequence
shortCircuitingErrors: Ior[Nec[String], List[Int]]] = Left(Chain(error1))
One way to accumulate both errors and successes is to convert all your Left cases into Both. One approach is using Option as right type and converting Left(errs) values into Both(errs, None). After calling .traverse, you end up with optList: List[Option] on the right side and you can flatten it with optList.flatMap(_.toList) to filter out None values.
class Error
class KeyValue
def find(key: String): Ior[Nel[Error], KeyValue] = ???
def findMultiple(keys: List[String]): Ior[Nel[Error], List[KeyValue]] =
keys
.traverse { k =>
val ior = find(k)
ior.putRight(ior.right)
}
.map(_.flatMap(_.toList))
Or more succinctly:
def findMultiple(keys: List[String]): Ior[Nel[Error], List[KeyValue]] =
keys.flatTraverse { k =>
val ior = find(k)
ior.putRight(ior.toList) // Ior[A,B].toList: List[B]
}
Say I have three database access functions foo, bar, and baz that can each return Option[A] where A is some model class, and the calls depend on each other.
I would like to call the functions sequentially and in each case, return an appropriate error message if the value is not found (None).
My current code looks like this:
Input is a URL: /x/:xID/y/:yID/z/:zID
foo(xID) match {
case None => Left(s"$xID is not a valid id")
case Some(x) =>
bar(yID) match {
case None => Left(s"$yID is not a valid id")
case Some(y) =>
baz(zID) match {
case None => Left(s"$zID is not a valid id")
case Some(z) => Right(process(x, y, z))
}
}
}
As can be seen, the code is badly nested.
If instead, I use a for comprehension, I cannot give specific error messages, because I do not know which step failed:
(for {
x <- foo(xID)
y <- bar(yID)
z <- baz(zID)
} yield {
Right(process(x, y, z))
}).getOrElse(Left("One of the IDs was invalid, but we do not know which one"))
If I use map and getOrElse, I end up with code almost as nested as the first example.
Is these some better way to structure this to avoid the nesting while allowing specific error messages?
You can get your for loop working by using right projections.
def ckErr[A](id: String, f: String => Option[A]) = (f(id) match {
case None => Left(s"$id is not a valid id")
case Some(a) => Right(a)
}).right
for {
x <- ckErr(xID, foo)
y <- ckErr(yID, bar)
z <- ckErr(zID, baz)
} yield process(x,y,z)
This is still a little clumsy, but it has the advantage of being part of the standard library.
Exceptions are another way to go, but they slow things down a lot if the failure cases are common. I'd only use that if failure was truly exceptional.
It's also possible to use non-local returns, but it's kind of awkward for this particular setup. I think right projections of Either are the way to go. If you really like working this way but dislike putting .right all over the place, there are various places you can find a "right-biased Either" which will act like the right projection by default (e.g. ScalaUtils, Scalaz, etc.).
Instead of using an Option I would instead use a Try. That way you have the Monadic composition that you'd like mixed with the ability to retain the error.
def myDBAccess(..args..) =
thingThatDoesStuff(args) match{
case Some(x) => Success(x)
case None => Failure(new IdError(args))
}
I'm assuming in the above that you don't actually control the functions and can't refactor them to give you a non-Option. If you did, then simply substitute Try.
I know this question was answered some time back, but I wanted to give an alternative to the accepted answer.
Given that, in your example, the three Options are independent, you can treat them as Applicative Functors and use ValidatedNel from Cats to simplify and aggregate the handling of the unhappy path.
Given the code:
import cats.data.Validated.{invalidNel, valid}
def checkOption[B, T](t : Option[T])(ifNone : => B) : ValidatedNel[B, T] = t match {
case None => invalidNel(ifNone)
case Some(x) => valid(x)
def processUnwrappedData(a : Int, b : String, c : Boolean) : String = ???
val o1 : Option[Int] = ???
val o2 : Option[String] = ???
val o3 : Option[Boolean] = ???
You can then replicate obtain what you want with:
//import cats.syntax.cartesian._
(
checkOption(o1)(s"First option is not None") |#|
checkOption(o2)(s"Second option is not None") |#|
checkOption(o3)(s"Third option is not None")
) map (processUnwrappedData)
This approach will allow you to aggregate failures, which was not possible in your solution (as using for-comprehensions enforces sequential evaluation). More examples and documentation can be found here and here.
Finally this solution uses Cats Validated but could easily be translated to Scalaz Validation
I came up with this solution (based on #Rex's solution and his comments):
def ifTrue[A](boolean: Boolean)(isFalse: => A): RightProjection[A, Unit.type] =
Either.cond(boolean, Unit, isFalse).right
def none[A](option: Option[_])(isSome: => A): RightProjection[A, Unit.type] =
Either.cond(option.isEmpty, Unit, isSome).right
def some[A, B](option: Option[A])(ifNone: => B): RightProjection[B, A] =
option.toRight(ifNone).right
They do the following:
ifTrue is used when a function returns a Boolean, with true being the "success" case (e.g.: isAllowed(userId)). It actually returns Unit so should be used as _ <- ifTrue(...) { error } in a for comprehension.
none is used when a function returns an Option with None being the "success" case (e.g.: findUser(email) for creating accounts with unique email addresses). It actually returns Unit so should be used as _ <- none(...) { error } in a for comprehension.
some is used when a function returns an Option with Some() being the "success" case (e.g.: findUser(userId) for a GET /users/userId). It returns the contents of the Some: user <- some(findUser(userId)) { s"user $userId not found" }.
They are used in a for comprehension:
for {
x <- some(foo(xID)) { s"$xID is not a valid id" }
y <- some(bar(yID)) { s"$yID is not a valid id" }
z <- some(baz(zID)) { s"$zID is not a valid id" }
} yield {
process(x, y, z)
}
This returns an Either[String, X] where the String is an error message and the X is the result of calling process.
Suppose I have few case classes and functions to test them:
case class PersonName(...)
case class Address(...)
case class Phone(...)
def testPersonName(pn: PersonName): Either[String, PersonName] = ...
def testAddress(a: Address): Either[String, Address] = ...
def testPhone(p: Phone): Either[String, Phone] = ...
Now I define a new case class Person and a test function, which fails fast.
case class Person(name: PersonName, address: Address, phone: Phone)
def testPerson(person: Person): Either[String, Person] = for {
pn <- testPersonName(person.name).right
a <- testAddress(person.address).right
p <- testPhone(person.phone).right
} yield person;
Now I would like function testPerson to accumulate the errors rather than just fail fast.
I would like testPerson to always execute all those test* functions and return Either[List[String], Person]. How can I do that ?
You want to isolate the test* methods and stop using a comprehension!
Assuming (for whatever reason) that scalaz isn't an option for you... it can be done without having to add the dependency.
Unlike a lot of scalaz examples, this is one where the library doesn't reduce verbosity much more than "regular" scala can:
def testPerson(person: Person): Either[List[String], Person] = {
val name = testPersonName(person.name)
val addr = testAddress(person.address)
val phone = testPhone(person.phone)
val errors = List(name, addr, phone) collect { case Left(err) => err }
if(errors.isEmpty) Right(person) else Left(errors)
}
Scala's for-comprehensions (which desugar to a combination of calls to flatMap and map) are designed to allow you to sequence monadic computations in such a way that you have access to the result of earlier computations in subsequent steps. Consider the following:
def parseInt(s: String) = try Right(s.toInt) catch {
case _: Throwable => Left("Not an integer!")
}
def checkNonzero(i: Int) = if (i == 0) Left("Zero!") else Right(i)
def inverse(s: String): Either[String, Double] = for {
i <- parseInt(s).right
v <- checkNonzero(i).right
} yield 1.0 / v
This won't accumulate errors, and in fact there's no reasonable way that it could. Suppose we call inverse("foo"). Then parseInt will obviously fail, which means there's no way we can have a value for i, which means there's no way we could move on to the checkNonzero(i) step in the sequence.
In your case your computations don't have this kind of dependency, but the abstraction you're using (monadic sequencing) doesn't know that. What you want is an Either-like type that isn't monadic, but that is applicative. See my answer here for some details about the difference.
For example, you could write the following with Scalaz's Validation without changing any of your individual validation methods:
import scalaz._, syntax.apply._, syntax.std.either._
def testPerson(person: Person): Either[List[String], Person] = (
testPersonName(person.name).validation.toValidationNel |#|
testAddress(person.address).validation.toValidationNel |#|
testPhone(person.phone).validation.toValidationNel
)(Person).leftMap(_.list).toEither
Although of course this is more verbose than necessary and is throwing away some information, and using Validation throughout would be a little cleaner.
As #TravisBrown is telling you, for comprehensions don't really mix with error accumulations. In fact, you generally use them when you don't want fine grained error control.
A for comprehension will "short-circuit" itself on the first error found, and this is almost always what you want.
The bad thing you are doing is using String to do flow control of exceptions. You should at all times use Either[Exception, Whatever] and fine tune logging with scala.util.control.NoStackTrace and scala.util.NonFatal.
There are much better alternatives, specifically:
scalaz.EitherT and scalaz.ValidationNel.
Update:(this is incomplete, I don't know exactly what you want). You have better options than matching, such as getOrElse and recover.
def testPerson(person: Person): Person = {
val attempt = Try {
val pn = testPersonName(person.name)
val a = testAddress(person.address)
testPhone(person.phone)
}
attempt match {
case Success(person) => //..
case Failure(exception) => //..
}
}
Starting in Scala 2.13, we can partitionMap a List of Eithers in order to partition elements based on their Either's side.
// def testName(pn: Name): Either[String, Name] = ???
// def testAddress(a: Address): Either[String, Address] = ???
// def testPhone(p: Phone): Either[String, Phone] = ???
List(testName(Name("name")), testAddress(Address("address")), testPhone(Phone("phone")))
.partitionMap(identity) match {
case (Nil, List(name: Name, address: Address, phone: Phone)) =>
Right(Person(name, address, phone))
case (left, _) =>
Left(left)
}
// Either[List[String], Person] = Left(List("wrong name", "wrong phone"))
// or
// Either[List[String], Person] = Right(Person(Name("name"), Address("address"), Phone("phone")))
If the left side is empty, then no elements were Left and thus we can build a Person out of the Right elements.
Otherwise, we return a Left List of the Left values.
Details of the intermediate step (partitionMap):
List(Left("bad name"), Right(Address("addr")), Left("bad phone"))
.partitionMap(identity)
// (List[String], List[Any]) = (List("bad name", "bad phone"), List[Any](Address("addr")))
I have posted quite several questions about failure handling in Scala and I really thank you all for your answers.
I understand my options when dealing with Either and Scalaz or a for comprehension, and I have another (last?) question:
How to do a fail-fast sequence of operations when the operations are dealing with the outside non-functional world, like a DB?
I mean I have a method like that:
def insertItem(item: Item): Either[Error,Item]
Thanks to Either and these answers, I know how to do it with Either: Chaining method calls with Either and
Method parameters validation in Scala, with for comprehension and monads
But my Item case class is immutable and it doesn't really make sense to return it as a Right since the caller already has the value.
Thus how can I do the same kind of thing with:
def insertItem(item: Item): Option[Error]
In my application, when an user is created, we also create some items for him.
If an item fails to create, then the whole process should stop.
When I use directly Option[Error] in a for comprehension, I don't think I'll get the result I expect.
I guess it makes sense to do something like that:
for {
_ <- insertItem(item1).toLeft("???").right
_ <- insertItem(item2).toLeft("???").right
_ <- insertItem(item3).toLeft("???").right
}
But as the values "???" I put in my Right are never useful, I guess I'm missing the elegant solution which do not involve creating Rights that will never be used.
I think I'm looking for something that will continue the for comprehension only when the result is None, which is kind of weird because I just want to continue to the next operation, and not do a real map operation.
By the way, if possible, I would like both non-Scalaz and Scalaz solutions.
I'm not sure Scalaz handle such things, because it seems more focused on real functional programming and perhaps do not provide code for side-effect behaviors like my use case?
I don't see a principled reason not to use Either[Error, Unit] in a case like this (or at least I've done it, and I don't feel guilty about it). Say we have the following:
def launch(thing: String): Either[String, Unit] = Either.cond(
thing.nonEmpty,
println("Launching the " + thing),
"Need something to launch!"
)
We can show that the right projection monad is appropriately lazy:
scala> for {
| _ <- launch("flowers").right
| _ <- launch("").right
| r <- launch("MISSILES").right
| } yield r
Launching the flowers
res1: Either[String,Unit] = Left(Need something to launch!)
No missiles get launched, as desired.
It's worth noting that if you use Option instead of Either, the operation you're describing is just the sum given the "First" monoid instance for Option (where the addition operation is just orElse). For example, we can write the following with Scalaz 7:
import scalaz._, Scalaz._
def fst[A](x: Option[A]): Option[A] ## Tags.First = Tag(x)
def launch(thing: String): Option[String] = if (thing.isEmpty) Some(
"Need something to launch!"
) else {
println("Launching the " + thing)
None
}
And now:
scala> val r: Option[String] = fst(launch("flowers")) |+| fst(
| launch("")) |+| fst(launch("MISSILES"))
Launching the flowers
r: Option[String] = Some(Need something to launch!)
Once again, no missiles.
We do something similar where I work with scalaz to fail fast with message sending, though this is not idiomatic scalaz:
def insertItem(item: Item): Validation[Error, Unit]
val result = for {
_ <- insertItem(item1)
_ <- insertItem(item2)
_ <- insertItem(item3)
} yield Unit
If you want to chain together methods that contain Option[Error] and only execute the next step if the Option is None, you can use orElse for that.
If you are only interested in the first failure, than an Iterator can be your choice:
case class Item(i: Int)
case class Error(i: Item)
val items = Iterator(1,3,7,-5,-2,0) map Item
def insertItem(item: Item): Option[Error] =
if (item.i < 0) Some(Error(item)) else None
scala> val error = (items map insertItem find (_.isDefined)).flatten
error: Option[Error] = Some(Error(Item(-5)))
If insertItem is not your only method it is possible to call them separated with your values:
val items = Iterator(
() => insertItem(i1),
() => deleteItem(i2),
() => insertItem(i3),
() => updateItem(i4))
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
I think you might benefit from using Lift's Box, which has Full (i.e. Some), Empty (i.e. None) and Failure (an Empty with a reason why it's empty and that can be chained). David Pollak has a good blog post introducing it. In short, you might do something like this (not tested):
def validate1: Box[String]
def validate2: Box[String]
def validate3: Box[String]
val validation = for (
validation1 <- validate1 ?~ "error message 1"
validation2 <- validate2 ?~ "error message 2"
validation3 <- validate3 ?~ "error message 3"
) yield "overall success message"
This isn't any shorter than the original example but it's, in my opinion, a bit more logical, with the result of a successful validation in a Full and a failed validation in Failure.
However, we can get smaller. First, since our validation function return Box[String], they can return Failures themselves and we don't need to transform Empty to Failure ourselves:
val validation = for (
validation1 <- validate1
validation2 <- validate2
validation3 <- validate3
) yield "overall success message"
But, Box also has an or method that returns the same Box if it is Full or the other Box if it is not. This would give us:
val validation = validate1 or validate2 or validate3
However, that line stops at the first validation success, not the first failure. It might make sense to make another method that does what you want (perhaps called unless?) though I can't say that it would really be much more useful than the for comprehension approach.
However, here's a little library pimping that does it:
scala> class Unless[T](a: Box[T]) {
| def unless(b: Box[T]) = {
| if (a.isEmpty) { a }
| else b
| }
| }
defined class Unless
scala> implicit def b2U[T](b: Box[T]): Unless[T] = new Unless(b)
b2U: [T](b: net.liftweb.common.Box[T])Unless[T]
scala> val a = Full("yes")
a: net.liftweb.common.Full[java.lang.String] = Full(yes)
scala> val b = Failure("no")
b: net.liftweb.common.Failure = Failure(no,Empty,Empty)
scala> val c = Full("yes2")
c: net.liftweb.common.Full[java.lang.String] = Full(yes2)
scala> a unless b
res1: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless b unless c
res2: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c unless b
res3: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c
res4: net.liftweb.common.Box[java.lang.String] = Full(yes2)
This is a quick hack based upon my limited understanding of Scala's type system, as you can see in the following error:
scala> b unless a
<console>:13: error: type mismatch;
found : net.liftweb.common.Full[java.lang.String]
required: net.liftweb.common.Box[T]
b unless a
^
However, that should be enough to get you on the right track.
Of course the Lift ScalaDocs have more information on Box.