Avoiding deeply nested Option cascades in Scala - scala

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.

Related

Do something when exactly one option is non-empty

I want to compute something if exactly one of two options is non-empty. Obviously this could be done by a pattern match, but is there some better way?
(o1, o2) match {
case (Some(o), None) => Some(compute(o))
case (None, Some(o)) => Some(compute(o))
case _ => None
}
You could do something like this:
if (o1.isEmpty ^ o2.isEmpty)
List(o1,o2).flatMap(_.map(x=>Some(compute(x)))).head
else
None
But pattern matching is probably the better way to go.
Thanks to helpful comments from #Suma, I came up with another solutions in addition to the current ones:
Since the inputs are always in the form of Option(x):
Iterator(Seq(o1,o2).filter(_!=None))
.takeWhile(_.length==1)
.map( x => compute(x.head.get))
.toSeq.headOption
Using iterator also allows for a sequence of values to be passed to the input. The final mapping will be done if and only if one value in the sequence is defined.
Inspired by now deleted answer of pedrofurla, which was attempting to use o1 orElse o2 map { compute }, one possibility is to define xorElse, the rest is easy with it:
implicit class XorElse[T](o1: Option[T]) {
def xorElse[A >: T](o2: Option[A]): Option[A] = {
if (o1.isDefined != o2.isDefined) o1 orElse o2
else None
}
}
(o1 xorElse o2).map(compute)
Another possibility I have found is using a pattern match, but using Seq concatenation so that both cases are handled with the same code. The advantage of this approach is it can be extended to any number of options, it will always evaluate when there is exactly one:
o1.toSeq ++ o2 match {
case Seq(one) => Some(compute(one))
case _ => None
}
Just initialize a sequence and then flatten
Seq(o1, o2).flatten match {
case Seq(o) => Some(compute(o))
case _ => None
}

Chaining validation in Scala

I have a Scala case class containing command-line configuration information:
case class Config(emailAddress: Option[String],
firstName: Option[String]
lastName: Option[String]
password: Option[String])
I am writing a validation function that checks that each of the values is a Some:
def validateConfig(config: Config): Try[Config] = {
if (config.emailAddress.isEmpty) {
Failure(new IllegalArgumentException("Email Address")
} else if (config.firstName.isEmpty) {
Failure(new IllegalArgumentException("First Name")
} else if (config.lastName.isEmpty) {
Failure(new IllegalArgumentException("Last Name")
} else if (config.password.isEmpty) {
Failure(new IllegalArgumentException("Password")
} else {
Success(config)
}
}
but if I understand monads from Haskell, it seems that I should be able to chain the validations together (pseudo syntax):
def validateConfig(config: Config): Try[Config] = {
config.emailAddress.map(Success(config)).
getOrElse(Failure(new IllegalArgumentException("Email Address")) >>
config.firstName.map(Success(config)).
getOrElse(Failure(new IllegalArgumentException("First Name")) >>
config.lastName.map(Success(config)).
getOrElse(Failure(new IllegalArgumentException("Last Name")) >>
config.password.map(Success(config)).
getOrElse(Failure(new IllegalArgumentException("Password"))
}
If any of the config.XXX expressions returns Failure, the whole thing (validateConfig) should fail, otherwise Success(config) should be returned.
Is there some way to do this with Try, or maybe some other class?
It's pretty straightforward to convert each Option to an instance of the right projection of Either:
def validateConfig(config: Config): Either[String, Config] = for {
_ <- config.emailAddress.toRight("Email Address").right
_ <- config.firstName.toRight("First Name").right
_ <- config.lastName.toRight("Last Name").right
_ <- config.password.toRight("Password").right
} yield config
Either isn't a monad in the standard library's terms, but its right projection is, and will provide the behavior you want in the case of failure.
If you'd prefer to end up with a Try, you could just convert the resulting Either:
import scala.util._
val validate: Config => Try[Config] = (validateConfig _) andThen (
_.fold(msg => Failure(new IllegalArgumentException(msg)), Success(_))
)
I wish that the standard library provided a nicer way to make this conversion, but it doesn't.
It's a case class, so why aren't you doing this with pattern matching?
def validateConfig(config: Config): Try[Config] = config match {
case Config(None, _, _, _) => Failure(new IllegalArgumentException("Email Address")
case Config(_, None, _, _) => Failure(new IllegalArgumentException("First Name")
case Config(_, _, None, _) => Failure(new IllegalArgumentException("Last Name")
case Config(_, _, _, None) => Failure(new IllegalArgumentException("Password")
case _ => Success(config)
}
In your simple example, my priority would be to forget monads and chaining, just get rid of that nasty if...else smell.
However, while a case class works perfectly well for a short list, for a large number of configuration options, this becomes tedious and the risk of error increases. In this case, I would consider something like this:
Add a method that returns a key->value map of the configuration options, using the option names as the keys.
Have the Validate method check if any value in the map is None
If no such value, return success.
If at least one value matches, return that value name with the error.
So assuming that somewhere is defined
type OptionMap = scala.collection.immutable.Map[String, Option[Any]]
and the Config class has a method like this:
def optionMap: OptionMap = ...
then I would write Config.validate like this:
def validate: Either[List[String], OptionMap] = {
val badOptions = optionMap collect { case (s, None) => s }
if (badOptions.size > 0)
Left(badOptions)
else
Right(optionMap)
}
So now Config.validate returns either a Left containing the name of all the bad options or a Right containing the full map of options and their values. Frankly, it probably doesn't matter what you put in the Right.
Now, anything that wants to validate a Config just calls Config.validate and examines the result. If it's a Left, it can throw an IllegalArgumentException containing one or more of the names of bad options. If it's a Right, it can do whatever it wanted to do, knowing the Config is valid.
So we could rewrite your validateConfig function as
def validateConfig(config: Config): Try[Config] = config.validate match {
case Left(l) => Failure(new IllegalArgumentException(l.toString))
case _ => Success(config)
}
Can you see how much more functional and OO this is getting?
No imperative chain of if...else
The Config object validates itself
The consequences of a Config object being invalid are left to the larger program.
I think a real example would be more complex yet, though. You are validating options by saying "Does it contain Option[String] or None?" but not checking the validity of the string itself. Really, I think your Config class should contain a map of options where the name maps to the value and to an anonymous function that validates the string. I could describe how to extend the above logic to work with that model, but I think I'll leave that as an exercise for you. I will give you a hint: you might want to return not just the list of failed options, but also the reason for failure in each case.
Oh, by the way... I hope none of the above implies that I think you should actually store the options and their values as an optionMap inside the object. I think it's useful to be able to retrieve them like that, but I wouldn't ever encourage such exposure of the actual internal representation ;)
Here's a solution that I came up with after some searching and scaladocs reading:
def validateConfig(config: Config): Try[Config] = {
for {
_ <- Try(config.emailAddress.
getOrElse(throw new IllegalArgumentException("Email address missing")))
_ <- Try(config.firstName.
getOrElse(throw new IllegalArgumentException("First name missing")))
_ <- Try(config.lastName.
getOrElse(throw new IllegalArgumentException("Last name missing")))
_ <- Try(config.password.
getOrElse(throw new IllegalArgumentException("Password missing")))
} yield config
}
Similar to Travis Brown's answer.

Scala: orElse on a component of a return value

I've been tasked with attaching an audit trail onto a bunch of calcuations for reconstruction of values after the fact (i.e. people with business domain knowledge to decipher what went wrong.) The current code looks something like this:
def doSomething = f(x) orElse g(x,y,z) orElse h(p,q,r) orElse default
Each of these returns an Option. The new code should return a tuple of (Option, Audit.)
I've implemented it as
def doSomething = f(x) match{
case None => g_prime(x,y,z)
case x # Some(_) => (x, SomeAuditObject)
}
//and taking some liberties with the actual signature...
def g_prime(x,y,z) = g(x,y,z) match{
and so on until the "default." Each function chains to the next and the next and so on. I don't like it. It feels way too imperative. I'm missing something. There's some way of thinking about this problem that I'm just not seeing. Other than wrapping the return values into another Option, what is it?
You can use Monads to compose transformations that leave an audit trail. You can compose the audits inside the Monad. Have a look at this answer for further details.
I tried to produce an example for you. I did not know how to handle the final step of the for-comprehension which is a map and provides no audit trail. If you disallow the use of map you cannot use for-comprehensions but have to use plain calls to flatMap.
case class WithAudit[A](value: A, audit: String){
def flatMap[B](f: A => WithAudit[B]): WithAudit[B] = {
val bWithAudit = f(value)
WithAudit(bWithAudit.value, audit + ":" + bWithAudit.audit)
}
def map[B](f: A => B): WithAudit[B] = {
WithAudit(f(value), audit + ":applied unknown function")
}
}
def doSomething(in: Option[Int]): WithAudit[Option[Int]] = WithAudit(
in.map(x => x - 23),
"substract 23"
)
def somethingElse(in: Int): WithAudit[String] = WithAudit(
in.toString,
"convert to String"
)
val processed = for(
v <- WithAudit(Some(42), "input Some(42)");
proc <- doSomething(v);
intVal <- WithAudit(proc.getOrElse(0), "if invalid, insert default 0");
asString <- somethingElse(intVal)
) yield asString
println(processed)
The output will be
WithAudit(
19,
input Some(42)
:substract 23
:if invalid, insert default 0
:convert to String
:applied unknown function
)
Safety
Using flatMap to process the value enforces the provision of an audit. If you don't provide map and limit how you can extract the value from the monad (maybe write a log output if you do so) you can be pretty safely assume that every transformation on the value will get logged. And when the value is obtained, you'll get an entry in your log.
Do you only audit successful executions of f, g, etc.?
If it is so, I'd make doSomething return Option[(YourData, Audit)] (instead of (Option[YourData], Audit)). You could then compose the functions like this:
def doSomething = (f(x) andThen (t => (t, audit_f(x)))) orElse
(g(x, y, z) andThen (t => (t, audit_g(x, y, z)))) orElse
... etc.

Scala: short form of pattern matching that returns Boolean

I found myself writing something like this quite often:
a match {
case `b` => // do stuff
case _ => // do nothing
}
Is there a shorter way to check if some value matches a pattern? I mean, in this case I could just write if (a == b) // do stuff, but what if the pattern is more complex? Like when matching against a list or any pattern of arbitrary complexity. I'd like to be able to write something like this:
if (a matches b) // do stuff
I'm relatively new to Scala, so please pardon, if I'm missing something big :)
This is exactly why I wrote these functions, which are apparently impressively obscure since nobody has mentioned them.
scala> import PartialFunction._
import PartialFunction._
scala> cond("abc") { case "def" => true }
res0: Boolean = false
scala> condOpt("abc") { case x if x.length == 3 => x + x }
res1: Option[java.lang.String] = Some(abcabc)
scala> condOpt("abc") { case x if x.length == 4 => x + x }
res2: Option[java.lang.String] = None
The match operator in Scala is most powerful when used in functional style. This means, rather than "doing something" in the case statements, you would return a useful value. Here is an example for an imperative style:
var value:Int = 23
val command:String = ... // we get this from somewhere
command match {
case "duplicate" => value = value * 2
case "negate" => value = -value
case "increment" => value = value + 1
// etc.
case _ => // do nothing
}
println("Result: " + value)
It is very understandable that the "do nothing" above hurts a little, because it seems superflous. However, this is due to the fact that the above is written in imperative style. While constructs like these may sometimes be necessary, in many cases you can refactor your code to functional style:
val value:Int = 23
val command:String = ... // we get this from somewhere
val result:Int = command match {
case "duplicate" => value * 2
case "negate" => -value
case "increment" => value + 1
// etc.
case _ => value
}
println("Result: " + result)
In this case, you use the whole match statement as a value that you can, for example, assign to a variable. And it is also much more obvious that the match statement must return a value in any case; if the last case would be missing, the compiler could not just make something up.
It is a question of taste, but some developers consider this style to be more transparent and easier to handle in more real-world examples. I would bet that the inventors of the Scala programming language had a more functional use in mind for match, and indeed the if statement makes more sense if you only need to decide whether or not a certain action needs to be taken. (On the other hand, you can also use if in the functional way, because it also has a return value...)
This might help:
class Matches(m: Any) {
def matches[R](f: PartialFunction[Any, R]) { if (f.isDefinedAt(m)) f(m) }
}
implicit def any2matches(m: Any) = new Matches(m)
scala> 'c' matches { case x: Int => println("Int") }
scala> 2 matches { case x: Int => println("Int") }
Int
Now, some explanation on the general nature of the problem.
Where may a match happen?
There are three places where pattern matching might happen: val, case and for. The rules for them are:
// throws an exception if it fails
val pattern = value
// filters for pattern, but pattern cannot be "identifier: Type",
// though that can be replaced by "id1 # (id2: Type)" for the same effect
for (pattern <- object providing map/flatMap/filter/withFilter/foreach) ...
// throws an exception if none of the cases match
value match { case ... => ... }
There is, however, another situation where case might appear, which is function and partial function literals. For example:
val f: Any => Unit = { case i: Int => println(i) }
val pf: PartialFunction[Any, Unit] = { case i: Int => println(i) }
Both functions and partial functions will throw an exception if called with an argument that doesn't match any of the case statements. However, partial functions also provide a method called isDefinedAt which can test whether a match can be made or not, as well as a method called lift, which will turn a PartialFunction[T, R] into a Function[T, Option[R]], which means non-matching values will result in None instead of throwing an exception.
What is a match?
A match is a combination of many different tests:
// assign anything to x
case x
// only accepts values of type X
case x: X
// only accepts values matches by pattern
case x # pattern
// only accepts a value equal to the value X (upper case here makes a difference)
case X
// only accepts a value equal to the value of x
case `x`
// only accept a tuple of the same arity
case (x, y, ..., z)
// only accepts if extractor(value) returns true of Some(Seq()) (some empty sequence)
case extractor()
// only accepts if extractor(value) returns Some something
case extractor(x)
// only accepts if extractor(value) returns Some Seq or Tuple of the same arity
case extractor(x, y, ..., z)
// only accepts if extractor(value) returns Some Tuple2 or Some Seq with arity 2
case x extractor y
// accepts if any of the patterns is accepted (patterns may not contain assignable identifiers)
case x | y | ... | z
Now, extractors are the methods unapply or unapplySeq, the first returning Boolean or Option[T], and the second returning Option[Seq[T]], where None means no match is made, and Some(result) will try to match result as described above.
So there are all kinds of syntactic alternatives here, which just aren't possible without the use of one of the three constructions where pattern matches may happen. You may able to emulate some of the features, like value equality and extractors, but not all of them.
Patterns can also be used in for expressions. Your code sample
a match {
case b => // do stuff
case _ => // do nothing
}
can then be expressed as
for(b <- Some(a)) //do stuff
The trick is to wrap a to make it a valid enumerator. E.g. List(a) would also work, but I think Some(a) is closest to your intended meaning.
The best I can come up with is this:
def matches[A](a:A)(f:PartialFunction[A, Unit]) = f.isDefinedAt(a)
if (matches(a){case ... =>}) {
//do stuff
}
This won't win you any style points though.
Kim's answer can be “improved” to better match your requirement:
class AnyWrapper[A](wrapped: A) {
def matches(f: PartialFunction[A, Unit]) = f.isDefinedAt(wrapped)
}
implicit def any2wrapper[A](wrapped: A) = new AnyWrapper(wrapped)
then:
val a = "a" :: Nil
if (a matches { case "a" :: Nil => }) {
println("match")
}
I wouldn't do it, however. The => }) { sequence is really ugly here, and the whole code looks much less clear than a normal match. Plus, you get the compile-time overhead of looking up the implicit conversion, and the run-time overhead of wrapping the match in a PartialFunction (not counting the conflicts you could get with other, already defined matches methods, like the one in String).
To look a little bit better (and be less verbose), you could add this def to AnyWrapper:
def ifMatch(f: PartialFunction[A, Unit]): Unit = if (f.isDefinedAt(wrapped)) f(wrapped)
and use it like this:
a ifMatch { case "a" :: Nil => println("match") }
which saves you your case _ => line, but requires double braces if you want a block instead of a single statement... Not so nice.
Note that this construct is not really in the spirit of functional programming, as it can only be used to execute something that has side effects. We can't easily use it to return a value (therefore the Unit return value), as the function is partial — we'd need a default value, or we could return an Option instance. But here again, we would probably unwrap it with a match, so we'd gain nothing.
Frankly, you're better off getting used to seeing and using those match frequently, and moving away from this kind of imperative-style constructs (following Madoc's nice explanation).

Using Either to process failures in Scala code

Option monad is a great expressive way to deal with something-or-nothing things in Scala. But what if one needs to log a message when "nothing" occurs? According to the Scala API documentation,
The Either type is often used as an
alternative to scala.Option where Left
represents failure (by convention) and
Right is akin to Some.
However, I had no luck to find best practices using Either or good real-world examples involving Either for processing failures. Finally I've come up with the following code for my own project:
def logs: Array[String] = {
def props: Option[Map[String, Any]] = configAdmin.map{ ca =>
val config = ca.getConfiguration(PID, null)
config.properties getOrElse immutable.Map.empty
}
def checkType(any: Any): Option[Array[String]] = any match {
case a: Array[String] => Some(a)
case _ => None
}
def lookup: Either[(Symbol, String), Array[String]] =
for {val properties <- props.toRight('warning -> "ConfigurationAdmin service not bound").right
val logsParam <- properties.get("logs").toRight('debug -> "'logs' not defined in the configuration").right
val array <- checkType(logsParam).toRight('warning -> "unknown type of 'logs' confguration parameter").right}
yield array
lookup.fold(failure => { failure match {
case ('warning, msg) => log(LogService.WARNING, msg)
case ('debug, msg) => log(LogService.DEBUG, msg)
case _ =>
}; new Array[String](0) }, success => success)
}
(Please note this is a snippet from a real project, so it will not compile on its own)
I'd be grateful to know how you are using Either in your code and/or better ideas on refactoring the above code.
Either is used to return one of possible two meaningful results, unlike Option which is used to return a single meaningful result or nothing.
An easy to understand example is given below (circulated on the Scala mailing list a while back):
def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] =
try {
Right(block)
} catch {
case ex => Left(ex)
}
As the function name implies, if the execution of "block" is successful, it will return "Right(<result>)". Otherwise, if a Throwable is thrown, it will return "Left(<throwable>)". Use pattern matching to process the result:
var s = "hello"
throwableToLeft { s.toUpperCase } match {
case Right(s) => println(s)
case Left(e) => e.printStackTrace
}
// prints "HELLO"
s = null
throwableToLeft { s.toUpperCase } match {
case Right(s) => println(s)
case Left(e) => e.printStackTrace
}
// prints NullPointerException stack trace
Hope that helps.
Scalaz library has something alike Either named Validation. It is more idiomatic than Either for use as "get either a valid result or a failure".
Validation also allows to accumulate errors.
Edit: "alike" Either is complettly false, because Validation is an applicative functor, and scalaz Either, named \/ (pronounced "disjonction" or "either"), is a monad.
The fact that Validation can accumalate errors is because of that nature. On the other hand, / has a "stop early" nature, stopping at the first -\/ (read it "left", or "error") it encounters. There is a perfect explanation here: http://typelevel.org/blog/2014/02/21/error-handling.html
See: http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html
As requested by the comment, copy/paste of the above link (some lines removed):
// Extracting success or failure values
val s: Validation[String, Int] = 1.success
val f: Validation[String, Int] = "error".fail
// It is recommended to use fold rather than pattern matching:
val result: String = s.fold(e => "got error: " + e, s => "got success: " + s.toString)
s match {
case Success(a) => "success"
case Failure(e) => "fail"
}
// Validation is a Monad, and can be used in for comprehensions.
val k1 = for {
i <- s
j <- s
} yield i + j
k1.toOption assert_≟ Some(2)
// The first failing sub-computation fails the entire computation.
val k2 = for {
i <- f
j <- f
} yield i + j
k2.fail.toOption assert_≟ Some("error")
// Validation is also an Applicative Functor, if the type of the error side of the validation is a Semigroup.
// A number of computations are tried. If the all success, a function can combine them into a Success. If any
// of them fails, the individual errors are accumulated.
// Use the NonEmptyList semigroup to accumulate errors using the Validation Applicative Functor.
val k4 = (fNel <**> fNel){ _ + _ }
k4.fail.toOption assert_≟ some(nel1("error", "error"))
The snippet you posted seems very contrived. You use Either in a situation where:
It's not enough to just know the data isn't available.
You need to return one of two distinct types.
Turning an exception into a Left is, indeed, a common use case. Over try/catch, it has the advantage of keeping the code together, which makes sense if the exception is an expected result. The most common way of handling Either is pattern matching:
result match {
case Right(res) => ...
case Left(res) => ...
}
Another interesting way of handling Either is when it appears in a collection. When doing a map over a collection, throwing an exception might not be viable, and you may want to return some information other than "not possible". Using an Either enables you to do that without overburdening the algorithm:
val list = (
library
\\ "books"
map (book =>
if (book \ "author" isEmpty)
Left(book)
else
Right((book \ "author" toList) map (_ text))
)
)
Here we get a list of all authors in the library, plus a list of books without an author. So we can then further process it accordingly:
val authorCount = (
(Map[String,Int]() /: (list filter (_ isRight) map (_.right.get)))
((map, author) => map + (author -> (map.getOrElse(author, 0) + 1)))
toList
)
val problemBooks = list flatMap (_.left.toSeq) // thanks to Azarov for this variation
So, basic Either usage goes like that. It's not a particularly useful class, but if it were you'd have seen it before. On the other hand, it's not useless either.
Cats has a nice way to create an Either from exception-throwing code:
val either: Either[NumberFormatException, Int] =
Either.catchOnly[NumberFormatException]("abc".toInt)
// either: Either[NumberFormatException,Int] = Left(java.lang.NumberFormatException: For input string: "abc")
in https://typelevel.org/cats/datatypes/either.html#working-with-exception-y-code