Use Either for failing in Scala code

Option Monad is a great expressive way to deal with something or anything in Scala. But what if you need to register a message when โ€œnothingโ€ happens? According to the Scala API documentation,

Any type is often used as an alternative to Scala.Option, where Left represents a rejection (by convention) and Correctly akin to Some.

However, I was not lucky to find best practices using Either or good real-world examples related to Either for handling failures. Finally, I came 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) } 

(Note that this is a snippet from a real project, so it wonโ€™t compile on its own)

I would be grateful that you use Either in your code and / or more advanced ideas for refactoring the above code.

+49
scala functional-programming either
Jul 28 '09 at 10:53
source share
4 answers

It is either used to return one of the possible two significant results, unlike Option, which is used to return one significant result or nothing.

The following is an easy to understand example (common on the Scala mailing list a back):

 def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] = try { Right(block) } catch { case ex => Left(ex) } 

As the name of the function indicates, if the execution of the "block" is successful, it will return "Right (<result>)". Otherwise, if Throwable is selected, it will return "Left (<throwable>)". Use the template 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 this helps.

+48
Jul 28 '09 at 14:35
source share

The Scalaz library has something similar. This is more idiomatic than Either for use as "getting either a valid result or failure."

Validation also allows you to accumulate errors.

Edit: "alike". Or completely false, because Validation is an applied functor, and scalaz. Or, named \ / (pronounced "disjonction" or "or"), is a monad. The fact that validation can accumulate errors is explained by this. On the other hand, / has a "stop-early" character, stopping at the first - \ / (read "left" or "error") that it encounters. There is a full 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 stated in the comment, copy / paste the link above (some lines are deleted):

 // 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")) 
+13
Feb 13 '11 at 16:12
source share

The scanned fragment seems very far-fetched. You use either in a situation where:

  • Itโ€™s not enough just to know that data is not available.
  • You need to return one of two different types.

Turning an exception into a left is indeed a common use case. Compared to try / catch, it has the advantage of keeping the code together, which makes sense if the exception is the expected result. The most common way to process data is to select a template:

 result match { case Right(res) => ... case Left(res) => ... } 

Another interesting way to handle Either is when it appears in a collection. When you draw a map over a collection, throwing an exception may not be practical, and you might want to return some information other than "impossible." Using Either allows you to do this without overloading 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, as well as a list of books without an author. Therefore, we can then 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 use goes like this. This is not a particularly useful class, but if you had seen it before. On the other hand, this is not useless either.

+7
Jul 28 '09 at 17:09
source share

Cats has a good way to create Either from code:

 val either: Either[NumberFormatException, Int] = Either.catchOnly[NumberFormatException]("abc".toInt) // either: Either[NumberFormatException,Int] = Left(java.lang.NumberFormatException: For input string: "abc") 

at https://typelevel.org/cats/datatypes/either.html#working-with-exception-y-code

0
Jan 23 '19 at 18:18
source share



All Articles