I have the following function:
private def constraintToJson(req: => Request[IO])
: EitherT[IO, Throwable, Unit]
= {
val err: EitherT[IO, Throwable, Unit] = EitherT.fromEither[IO](Left(new Exception("Not JSON format request.")))
req.contentType match {
case Some(s) =>
if (s != `Content-Type`(MediaType.`application/json`))
err
else
EitherT.fromEither[IO](Right(()))
case None =>
err
}
}
The question is, it is wrong to return a Unit if it is right or there is another choice?
I think that returning Unit (wrapped into Either/EitherT) might be OK if this is a kind of the final step of your computation and it actually does not produce any output. In other cases (most probably including yours) you should return some value for the successful case so that you can chain it further. So the main question is: how constraintToJson is supposed to be used? The obvious suspect for your case is returning Request or its body if the Content-Type matches JSON because this is most probably the data that will be used by the next step.
Simplified Question
Let me abstract your problem first by removing IO.
The question is whether a method with a signature like the following is useful:
def validate[A](a: Request[A]): Either[Error,()] =
if(isOk(a.someProperty)) Left(()) else Right("invalid")
Noting that Either[A,()] =:= Option[A], this method checks for the presence of any Error in the value of the request, and returns this Error, if detected.
What could be done with such a method?
Check and exit
We could write a program that checks a Request:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest).toLeft.foreach{error =>
println("invalid request: " + error
System.exit(-1)
}
System.exit(0)
In this case validate has a useful signature.
Check and go on
We could also validate a request, and then go on to execute it:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(_) =>
println("executing request: " + someRequest.execute)
System.exit(0)
}
While this code works perfectly well, it throws away an argument (_) in the second match clause. This is an indicator of some bad design. The code within the case Right(_) takes the value someRequest from outside, and treats it as a valid request because of the way you wrote your code.
If we would call someReuqest.execute within the Left case, we would probably end up with a runtime exception when executing an invalid Request.
Depending on our desired level of rigor, this can be a code smell.
We can improve the code in the following way.
Using a Non-Unit return type
We can circumvent returning Unit by simply returning the checked argument.
This appears to be a bit redundant, and we shall later see how to turn the returned value into something useful.
But let's first look at this option.
def validateTyped[A](a: Request[A]): Either[Error,Request[A]] =
if(isOk(a.someProperty)) Left(a) else Right("invalid")
Then we can write the Check and go on code as
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequestTyped) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
//we now execute the validated request
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
Now this improves the code a bit.
We don't throw away a returned value anymore in the second match clause.
We can execute the validatedRequest, because we've read in the documentation of validateRequest that a returned Right value is well formed, and it may be executed without error.
Looks good, right?
But we can do better.
Prevent execution of malformed requests
We can still improve it further by totally preventing executing a malformed Request by changing our Request type.
case class Request[A](query: String){
def validate: Either[Error,ValidRequest[A]] = ???
}
case class ValidRequest[A](query: String){
def execute: A
}
With this code, it is not possible to call Request.execute anymore, as a validation now becomes mandatory.
Otherwise the code will not compile.
We also note that the Right value returned by Request.validate is now needed.
It has become the only source of obtaining a ValidRequest.
Now it is impossible to call execute a Request that isn't validated, yet:
val someRequest: Request[X] = getRequestFrom("somewhere")
someRequest.validate match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
Conclusion
In this way we have turned a strange looking method that returned Unit into a method that returns a meaningful and needed value.
In addition it has become impossible to execute a Request that has not been validated successfully.
Related
We have some legacy code in our codebase that is eventually going to be refactored to use Validated and Either from Cats library. This is because Validated does not use fail-fast mechanics. The unrefactored code uses fail-fast mechanics of Try monad.
Since the refactoring hasn't happened yet, I am doing a kludgy hack to get around the fact that the Try monad is fail-fast. I am having trouble implementing it however.
I basically have a list of type Try[T] that is guaranteed to all be Failures.
I am trying to aggregate all of the error messages of all the Failures into a single Failure.
Here is the function I am refactoring:
private def extractTry[T](xs: IndexedSeq[Try[T]]): Try[IndexedSeq[T]] = {
val failures = xs.collect { case Failure(ex) => Failure(ex) }
if (failures.size > 0) failures.head
else Success(xs.map(_.get))
}
Instead of failures.head in the second line of the method, I want to aggregate all the Failures.
So something like
if (failures.size > 0) failures.foldLeft(Failure(new IllegalArgumentException(""))){case (Failure(acc), Failure(e)) => Failure(new IllegalArgumentException(acc.getMessage + e.getMessage))}
The only thing I don't like about this implementation is that I would like each step of fold not to use IllegalArgumentException, but to use the new element's exception type. So the idea is to keep the exception type of the last element in failures, and not to use an arbitrary exception type.
We are planning to eventually use Either[Throwable, T] in place of Try and will probably run into the exact same problem there when we try to aggregate errors. We want to keep the exception type and not assign an arbitrary one like IllegalArgumentException. So this problem is going to have to be solved sooner or later, and I would prefer that it be sooner.
Does anyone have any suggestions? Any help would be appreciated.
Ideally, we would follow suggestion by #Luis. Until then consider perhaps something like so
sealed trait OverallResult[+T]
case class OverallError(accumulatedMessage: String, finalErrorCode: Int) extends OverallResult[Nothing]
case class OverallSuccess[T](xs: IndexedSeq[T]) extends OverallResult[T]
object OverallResult {
/**
* Aggregating over a chain of Failures, it will only keep the exception type of the last Failure.
* This is just a heuristic to decide on the error code. Depending on the exception type, we use
* a different error code. So NoSuchElementException is 404 and IllegalArgumentException is 400.
*/
def apply[T](xs: IndexedSeq[Try[T]]): OverallResult[T] = {
val failures = xs.collect { case Failure(ex) => ex }
if (failures.nonEmpty) {
val accMessage = failures.map(_.getMessage).mkString("[", ",", "]")
OverallError(accMessage, errorCode(failures.last))
}
else OverallSuccess(xs.map(_.get))
}
private def errorCode(ex: Throwable): Int = ex match {
case _: NoSuchElementException => 404
case _: IllegalArgumentException => 400
case e => throw new RuntimeException("Unexpected exception. Fix ASAP!", e)
}
}
OverallResult(Vector(Try(throw new NoSuchElementException("boom")), Try(throw new IllegalArgumentException("crash"))))
OverallResult(Vector(Try(42), Try(11)))
which outputs
res0: OverallResult[Nothing] = OverallError([boom,crash],400)
res1: OverallResult[Int] = OverallSuccess(Vector(42, 11))
Note explicit documentation of the heuristic mentioned in the comments:
/**
* Aggregating over a chain of Failures, it will only keep the exception type of the last Failure.
* This is just a heuristic to decide on the error code. Depending on the exception type, we use
* a different error code. So NoSuchElementException is 404 and IllegalArgumentException is 400.
*/
Error accumulation is simulated with
failures.map(_.getMessage).mkString("[", ",", "]")
and overall status code decided with
errorCode(failures.last)
Now clients of extractTry need to be refactored to pattern match on OverallResult ADT, and finalErrorCode instead of exceptions, but lower level codebase should remain unaffected.
Mario wrote up a response that I think deserves being the accepted answer because of its thoroughness. It was while reading his answer that I stumbled upon another solution that requires less code change, but still gets the job done.
The answer is to pattern match on the exception type, which seems very obvious in retrospect.
if (failures.size > 0) failures.foldLeft(Failure(new IllegalArgumentException(""))){case (Failure(acc), Failure(e)) =>
val message = acc.getMessage + e.getMessage
e match {
case ex: IllegalArgumentException => Failure(new IllegalArgumentException(message))
case ex: NoSuchElementException => Failure(new NoSuchElementException(message))
}
}
Tried googling variations on this trivial question but didn't get an answer...
Basically I have a pattern match in my receive method.
In some cases I want to break early from the receive handling
override def receive = {
case blah => {
... preflight code
if (preflight failed) {
sender() ! errorMSG
"break" or "return" here // get error "method receive has a return statement ; needs result type -
// I tried adding Unit to the receive and return statements
}
... more code
....
if (something happened) {
sender() ! anotherErrorMSG
"break" or "return" here
}
...
}
case foo => {...}
case bar => {...}
} // end receive
See this discussion of return's semantics and remember that receive returns a PartialFunction[Any, Unit] which is then evaluated after receive has returned. In short, there's no way to return early.
Ă–mer Erden's solution of throwing an exception and using actor supervision works (indeed, exception throwing with all of its overhead is basically the only way to reliably end a computation early), but if you need any state to carry over from message to message, you'll need Akka persistence.
If you don't want to nest if-elses as in chunjef's solution, you can use context.become and stash to create some spaghetti-ish code.
But the best solution may be to have the things that might fail be their own functions with Either result types. Note that the Either API in scala 2.12 is quite a bit nicer than in previous versions.
import scala.util.{ Either, Left, Right }
type ErrorMsg = ...
type PreflightSuccess = ... // contains anything created in preflight that you need later
type MoreCodeSuccess = ... // contains anything created in preflight or morecode that you need later
def preflight(...): Either[ErrorMsg, PreFlightSuccess] = {
... // preflight
if (preflight failed)
Left(errorMsg)
else
Right(...) // create a PreflightSuccess
}
def moreCode1(pfs: PreFlightSuccess): Either[ErrorMsg, MoreCodeSuccess] = {
... // more code
if (something happened)
Left(anotherErrorMSG)
else
Right(...) // create a MoreCodeSuccess
}
def moreCode2(mcs: MoreCodeSuccess): Either[ErrorMsg, Any] = {
... // more code, presumably never fails
Right(...)
}
override def receive = {
case blah =>
val pf = preflight(...)
val result = pf.map(morecode1).joinRight.map(moreCode2).joinRight // only calls morecode1 if preflight succeeded, and only calls morecode2 if preflight and morecode1 succeeded
result.fold(
{ errorMsg => sender ! errorMsg },
()
)
case foo => ...
case bar => ...
}
Whether this is preferable to nested if-else's is a question of taste...
This may not be your question's exact answer but in your case adding supervisor actor would be the better solution. In Akka Supervision model convince you to handle exceptions on supervisor actor instead of sending error messages back to the sender.
This approach brings you a fault-tolerant model and also you can throw exception at any line you want(which solves your current problem), your supervisor actor will handle the throwable with restarting, resuming or stopping the child actor.
please check the link
I have a case class QueryParamsas follows:
case class QueryParams(
limit: Option[Integer] = None,
refresh: Option[Boolean] = None,
organisationalUnit: Option[String] = None)
These values limit,refresh,organisationalUnit are actually passed as query parameters in request url for play application.
I need to write a code to check if request URL contains any value for organisationalUnit and if yes I need to throw error .If no, I need to proceed with further operations.
Can anyone help me here
Options are quite good for this kind of thing:
val params: QueryParams = ???
params.organizationalUnit.foreach(_ => throw new Exception("your error message"))
In this way you'll throw only if organizationalUnit is defined. You can also express it as follows:
for (_ <- params.organizationalUnit) {
throw new Exception("your error message")
}
Or alternatively:
if (params.organizationalUnit.isDefined) {
throw new Exception("your error message")
}
The latter is probably the most readable, even though it may not be recognized as very idiomatic according to certain coding styles.
The answer from stefanobaghino is good but I prefer pattern matching for such cases:
params.organisationalUnit match {
case Some(_) => // processing
case _ => //logging
}
If you need other values you can match the whole instance
params match {
case QueryParams(Some(limit), Some(refresh), Some(organisationalUnit)) =>
case QueryParams(mayBeLimit, mayBeRefresh, Some(organisationalUnit)) =>
case _ =>
}
I have a controller that exposes a method as a route. In this method, I call a long running computation that returns a Future[SomeType].
I now have the following:
def compute(id: String) = Action.async { request =>
val result: Future[SomeType] = compute(id)
result.map(value => Ok(transform(value, id)))
}
So far this is just the happy path. What if compute(id) results in a Failure? How to handle that? I could wrap the whole thing in a Try block, but is there a better alternative? Any suggestions?
We usually use the following pattern:
def compute(id: String) = Action.async { request =>
val result: Future[SomeType] = compute(id)
result.map(value => Ok(transform(value, id)))
.recover { case ex =>
Logger.error("Something went wrong", ex)
InternalServerError
}
}
This way the HTTP response code will be 500 INTERNAL SERVER ERROR, so the caller will be informed. You may also want to add validation on the parameters of the request and return a 400 BAD REQUEST etc.
Scala's Try is very useful.
I'd like to use that pattern, but log all exceptions.
How can I do this?
Define the following helper:
import scala.util.{Try, Failure}
def LogTry[A](computation: => A): Try[A] = {
Try(computation) recoverWith {
case e: Throwable =>
log(e)
Failure(e)
}
}
Then you can use it as you would use Try, but any exception will be logged through log(e).
Starting Scala 2.13, the chaining operation tap can be used to apply a side effect (in this case some logging) on any value while returning the original value:
import util.chaining._
val x = Try("aa".toInt).tap(_.failed.foreach(println))
// java.lang.NumberFormatException: For input string: "aa"
// x: Try[Int] = Failure(java.lang.NumberFormatException: For input string: "aa")
Or an equivalent pattern matching version:
val x = Try("aa".toInt).tap { case Failure(e) => println(e) case _ => }
// java.lang.NumberFormatException: For input string: "aa"
// x: Try[Int] = Failure(java.lang.NumberFormatException: For input string: "aa")
The tap chaining operation applies a side effect (in this case println or some logging) on a value (in this case a Try) while returning the original unmodified value on which tap is applied (the Try):
def tap[U](f: (A) => U): A
You can tweak it even further using implicit class
def someMethod[A](f: => A): Try[A] = Try(f)
implicit class LogTry[A](res: Try[A]) {
def log() = res match {
case Success(s) => println("Success :) " + s); res
case Failure(f) => println("Failure :( " + f); res
}
}
Now you can call someMethod and on its result call log like this:
scala> someMethod(1/0).log
Failure :( java.lang.ArithmeticException: / by zero
and
scala> someMethod(1).log
Success :) 1
Of course println method inside implicit class can be substituted with any logging you want.
You used the term "exceptions" which is ambiguous. (java.lang.)Throwable is the root of anything that can be placed behind the throw term. java.lang.Exception is one of the two descendants of Throwable (the other being java.lang.Error). Further making this ambiguous is java.lang.RuntimeException, a descendant of Exception, which is probably where you mostly want to spend your logging time (unless you are doing lower level application framework or hardware driver implementations).
Assuming you are wanting to log literally ALL instances of Throwable, then you would need something like this (NOT RECOMMENDED):
def logAtThrowable(f: => A): Try[A] =
try
Try(f) match {
case failure # Failure(throwable) =>
log(s"Failure: {throwable.getMessage}")
failure
case success # _ =>
//uncomment out the next line if you want to also log Success-es
//log(s"Success: {throwable.getMessage}")
success
}
catch throwable: Throwable => {
//!NonFatal pathway
log(s"Failure: {throwable.getMessage}")
throw throwable
}
The external try/catch is required to capture all the Throwable instances which are filtered away by scala.util.control.NonFatal within the Try's try/catch block.
That said...there is a Java/JVM rule: you should never define a catch clause at the resolution of Throwable (again, unless you are doing lower level application framework or hardware driver implementations).
Following the intention of this rule, you would need to narrow the Throwable to you only emitted logging at the finer grained level, say something more refined, like java.lang.RuntimeException. If so, the code would look like this (recommended):
def logAtRuntimeException(f: => A): Try[A] =
Try(f) match {
case failure # Failure(throwable) =>
throwable match {
case runtimeException: RuntimeException =>
log(s"Failure: {runtimeException.getMessage}")
}
failure
case success # _ =>
success
}
In both code snippets above, you will notice that I used match as opposed to .recoverWith. This is to facilitate easily adding a rethrow that works. It turns out that all the methods on Try are themselves also wrapped with try/catch blocks. This means that if you want to log the Throwable and then rethrow it, if you are using one of the Try methods like recoverWith, the rethrow is immediately recaught and placed into a Failure thereby completely undermining the value of the intentional rethrow. By using match, the rethrow is guaranteed to succeed as it remains outside any of the Try methods.
If you would like to see more of the rabbit holes around this particular area, I created a blog post of my own exploration.