I have a Neo4J query that returns a list of nodes that most likely wont be empty, but in some cases could return null. How can I check for a null result within a map or flatmap operation?
val nodes = {
storeAPI.NeoQuery(parentNodesIDs).resultList().map {
_.flatMap {
record =>
record.get("assetList").asList.asScala.map(_.toString).toSet
}
}.recover {
case e: Exception =>
logger.error(s"Failure in getSimplifiedAssetListFromContainer: ", e)
throw e
}
}
I have tried to take the storeAPI.NeoQuery(parentNodeIDs).resultList() into a val outside of the above block, but then val nodes goes out of scope.
val nodes = {...} is of type Future[List[String]]
Any help would be great!
Try flatMap(Option(_)) like so
record
.get("assetList")
.asList
.asScala
.flatMap(Option(_))
.map(_.toString)
.toSet
For example
List(1,null,3).flatMap(Option(_)).map(_.toString).foreach(println)
outputs
1
3
This works because Option(null) is None, and Nones get discarded by flatMap.
Related
I am trying to count number of records, and if i get them return the records else just log the error.
Following is the snippet:
val records: Any = Try(count()) match {
case Success(records) => records
case Failure(exception) => logger.error(s"Exception occurred")
}
The data type of count method is Int but because of wrapping it in Try, getting type as any.
How can i solve the problem?
what is a return type of count method / field returns. If you want just ignore the exception and print it then continue, you should have done something like this:
val records: List[Int] = Try(count()) match {
case Success(records) => records
case Failure(exception) =>
log.error("Exception occurred", exception)
List.empty
}
def count(): List[Int] = List(1, 2 ,3)
I used traverse to execute a collection of futures like this:
val result: Future[List[Either[Error, Int]]] = Future.traverse(urls)(foo(_))
I end up with a Future[List[Either[Error, Int]]]. How can I check that one of these futures resulted in an Error?
I tried to do this but I think it is wrong because I am reading that you cannot substitute variables for futures?
val check: Future[Boolean] = result.map{
fut => fut.exists(c => c.isLeft)
}
check.map{
b => b match {
case true => // do something
case false => // do something
}
}
You can convert the result to a list of errors like this:
val errors: Future[List[Error]] = result.map(_.collect{ case Left(err) => err })
It is then possible to use Await.result to extract these error values, but that is nearly always a bad idea because it blocks the current thread.
It is better to ask "What do I want to do once the Future is complete but returns errors?". Then implement that behaviour in a map or foreach on the errors Future.
I have two functions: one returns a Future[Thing Or Exception] and another that returns Future[Boolean. I want a function that calls both and returns Future[Thing Or Exception]. If the boolean function returns false I want to return an exception, else the return of the other function.
I have code like this but a) I hate the cast and b) when run on the "boolean gets true" path I get this error when I eventually Await.result on the return in my test code: "Promise$DefaultPromise cannot be cast to org.scalatic.Or".
def thingFuture: Future[Thing Or Exception]
def boolFuture: Future[Boolean]
def combineFutures: Future[Thing Or Exception] = {
val result = boolFuture.map {x =>
x match {
case true => thingFuture
case false => Exception
}
}
// without the cast compiler says result is of type Future[Object]
result.asInstanceOf[Future[Thing Or Exception]]
}
I've also tried this but it gets the same Promise error on the success path
def combineFutures: Future[Thing Or Exception] = {
val result = boolFuture.map {x =>
x match {
case true => thingFuture.map { y =>
y match {
case Good(thing) => thing
case Bad(exception) => exception
}
case false => Exception
}
}
}
Can anyone tell me how to compose two futures with different return types? Thanks!
Every future can be completed with failed state in case exception has occurred, so you can simply return thingFuture in the "happy path" and throw an exception in case boolean is false. This will return a Future.failed with the underlying exception.
val result = boolFuture.flatMap {x =>
x match {
case true => thingFuture
case false => throw new Exception("whatever")
}
}
Note the flatMap instead of map. Because we map the underlying value of one future into a yet another future, by using simple map we would wind up with Future[Future[Thing]].
Also note that instead of throwing an exception, you could also return a Future.failed(throw new Exception("whatever")) and the result would be the same - in both case you get a failed future.
EDIT: I just realized Or comes from scalactic, which I never used, but the philosophy remains the same. You need to flatMap your boolean future and your ThingOrException future in order to wind up with Future[ThingOrException]. If you ever find yourself in a situation where you need to flatMap a Future, but one of the case clauses returns an ordinary value (e.g. in case of true return Future[Thing], in case of false return just Exception) then you can wrap the ordinary value into a future. This way all branches return a future and flatMap will work correctly. For example:
val someOtherFuture = Future(43)
val someOrdinaryValue = 44
Future(someInteger).flatMap {
case 42 => someOtherFuture
case _ => Future(someOrdinaryValue)
}
In order to simplify things for the runtime machinery a bit, you can also write Future.successful(someOrdinaryValue) in which case no background computation is started.
As far as I can tell from Scalatic documentation, you can get an instance of Right Or Left by either Good(Right) or Bad(Left).
That means the composition can potentially look like this:
boolFuture.flatMap(b => if (b) thingFuture else Future.successful(Bad(new Exception())))
The types should unify to Future[Or[Thing, Exception]]
effective scala style states the following:
this is both succinct and correct, but nearly every reader will have a
difficult time recovering the original intent of the author. A
strategy that often serves to clarify is to name intermediate results
and parameters:
val votesByLang = votes groupBy { case (lang, _) => lang }
val sumByLang = votesByLang map { case (lang, counts) =>
val countsOnly = counts map { case (_, count) => count }
(lang, countsOnly.sum)
}
val orderedVotes = sumByLang.toSeq
.sortBy { case (_, count) => count }
.reverse
what I fail to understand is in this style if i have errors while evaluating the value for votesByLang, sumByLang, ... how can I have a single recover at the end after all if i just had .map.map ... I could have a single recover at the end. So is it possible to have a single recover also with this style? (and not only possible but with good style...)
Or in other words what is the succinct and correct error handling in this succinct and correct functional pipelining code?
you see with the non-concise version i'm able to code this:
object MyRealMainObj extends App {
println(
Try(1)
.map(doOne)
.map(doTwo)
.recover { // non succinct version can catch any prior error how to achieve the same in succinct version
case e: Throwable => println("recovering from: " + e.getMessage)
}
)
def doOne(i: Int): Int = { i + 1; throw new RuntimeException("failed in one") }
def doTwo(i: Int): Int = { i + 2; throw new RuntimeException("failed in two") }
}
a single recover will catch any previous error in the .map but with the concise-succinct how can I achieve the same in concise and succinct way?
It sound like you expect some sort of scope to be magically created by .recover. This is not the case. With some minor simplifications, Try(expr) is
try {
val result = expr
Success(result)
catch {
e => Failure(e)
}
tryResult.map(f) is :
tryResult match {
case Success(x) => Try(f(x))
case f: Failure => f
}
tryResult.recover(f) is
tryResult match {
case Failure(e) if f.isDefinedAt(e) => Try(f(e))
case other => other
}
There is no special magic. At every stage, you have values, not code in the scope of some try/catch. Putting those values in val will not change a thing. If you "debug by hand", you will notice that you get a Success object, then at some point, one of your map may fail and return a Failure, the following ones will keep being failures, and then when you pass the last one to recover, it may go back to Success. Settings vals in between cannot make any difference.
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