Is there a full specification for pattern matching possibilities of Scala?
I am unable to fix following code:
something match {
case e # (sae: ServerApiException if sae.statusCode == 401 | _: UnauthorizedException) => {
doSomething(e)
}
...
}
(It does not compile in 2.8.1.)
I'm not sure I'd write the code this way; it's hard to follow (in addition to not working in its original form).
I'd rather go for something like
def doSomething(e: Exception) = { /* whatever */ }
something match {
case sae: ServerApiException if (sae.statusCode == 401) => doSomething(sae)
case ue: UnauthorizedException => doSomething(ue)
}
to avoid duplicate code. Or you could use options:
(something match {
case sae: ServerApiException if (sae.statusCode == 401) => Some(sae)
case ue: UnauthorizedException => Some(ue)
case _ => None
}).foreach(e => /* do something */ )
if you prefer to write the method afterwards. But I think the first way is likely the clearest.
Chapter 8 of the Scala Language Spec? (pdf).
More concretely, this answer may be of some assistance, that is you should be able to do something like:
case e: Exception if e.isInstanceOf[UnauthorizedException] || (e.isInstanceOf[ServerApiException] && e.asInstanceOf[ServerApiException].statusCode == 401) => {
doSomething(e)
}
Finally, I managed that with help of Scala Language Spec (Scala Syntax Summary):
something match {
case e: Exception if (e match {
case sae: ServerApiException if sae.statusCode == 401 => true
case _: UnauthorizedException => true
case _ => false
}) => {
doSomething(e)
}
...
}
Related
I have some API Rest with CRUD operations.
Each entity is identified by using UUID.
For example for Create it is similar to this:
private val createProduct = post {
path("product" / Segment) { productUUID =>
Try(UUID.fromString(productUUID)) match {
case Failure(_) => // Bad request
case Success(validUuid) =>
entity(as[ProductData]) { productData =>
onComplete(addProduct(validUuid, productData)) {
case Success(_) => complete(StatusCodes.OK)
case Failure(ex) => // some code
}
}
}
}
}
Operations Read(GET), Update(PUT) and Delete(DELETE) are similar to POST:
first step is get the uuid
for PUT get the payload using entity(as[ProductData]) (not needed for GET and DELETE)
invoke some method that returns Future[Something]
What I would like to do is remove boilerplate like:
getting the UUID, validating it, returning Bad Request if it's not valid
create some directive/function for handling the future: if there's an exception just return 500 Internal Server Error, but in case of Success continue for processing the value (I think using provide directive).
I found this example (https://fmsbeekmans.com/blog/akka-http-2-creating-custom-directives.html):
def simplifiedOnComplete[T](future: Future[T])(timeout: FiniteDuration): Directive1[T] = {
Try(Await.result(future, Duration.Inf)) match {
case Success(result) => provide(result)
case Failure(error) => failWith(error)
}
}
I said, ok, there's a try in this example! Maybe I can change it for working with UUID instead of Future:
def getUUID[T]: Directive1[T] = {
path(Segment) { maybeuuid =>
Try(UUID.fromString(maybeuuid)) match {
case Success(result) => provide(result) // compilation error here
case Failure(error) => failWith(error)
}
}
}
The code does not compile with the error:
Type mismatch. Required: Route, found: Directive1[UUID]
I guess the problem is I've added path ...
How can I create a Directive to extract the valid uuid and return Bad Request if it's not valid?
And, is it possible to encapsulate in a custom directive the code that handles de future?
For example the routes defined at the top would be something like:
private val createProduct = post {
path ("product") {
extractUUID { validUUID =>
entity(as[ProductData]) { productData =>
futureSuccess(addProduct(validUUID, productData)) { successValue =>
// code here, for example: complete(OK)
}
}
}
}
}
You were almost there - Your code is :
def getUUID[T]: Directive1[T] = {
path(Segment) { maybeuuid =>
Try(UUID.fromString(maybeuuid)) match {
case Success(result) => provide(result) // compilation error here
case Failure(error) => failWith(error)
}
}
}
You don't need generic T since you can only have a UUID back from UUID.fromString
path(Segment) gives you a Directive. So you want to useflatMap to get a Directive back (provide returns a Directive)
So it would be something like
def getUUID: Directive1[UUID] = {
path(Segment).flatMap { maybeuuid =>
Try(UUID.fromString(maybeuuid)) match {
case Success(result) => provide(result)
case Failure(error) => failWith(error)
}
}
}
And, is it possible to encapsulate in a custom directive the code that
handles de future?
Yes, it is the same as the above. onComplete returns a Directive so you will have to flatMap.
To return BadRequest, look up the rejection handlers in the akka-http doc.
Consider following sample code
try{
.....
}catch{
case e1:Exception1 => { method1(..)}
case e2:Exception2 => { method2(..)}
case e3:Exception3 => { method3(..)}
}
Now if I'd like to run same method i.e. methodGeneral() to all exception (e1, e2, e3)
however how to avoid to apply the method in each block like below, but can achieve in one shot?
try{
.....
}catch{
case e1:Exception1 => { method1(..); methodGeneral();}
case e2:Exception2 => { method2(..); methodGeneral();}
case e3:Exception3 => { method3(..); methodGeneral();}
}
The Scala Try type offers a few more features than the basic try..catch. For example, you could do something like this.
import scala.util.Try
Try {
. . .
}.recover{ excp:Throwable =>
excp match {
case e1:Exception1 => method1(..)
case e2:Exception2 => method2(..)
case e3:Exception3 => method3(..)
}
methodGeneral(excp)
}.get
This requires that the .recover code block returns the same type as the Try code block. If the return value is actually of no interest, such as Unit for example, then the .get can be omitted.
If you're really wedded to the old try..catch you might try something like this.
try {
. . .
} catch {
case excp: Throwable =>
excp match {
case e1:Exception1 => method1(..)
case e2:Exception2 => method2(..)
case e3:Exception3 => method3(..)
}
methodGeneral(excp)
}
It's still a good idea to ensure that the try block and catch block are type-compatible.
I have a function convert date time to epoch, and I want to put some logs where it failed. Such as case _ => and .getOrElse(JNothing) , how can I do that?
def time_to_epoch(params: List[JValue]): JValue = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
***
}.getOrElse(JNothing) // for saying something similar to can't convert time to epoch
case _ =>
JNothing // Add a (ErrorMessage(xxxxxxx)) for saying number of parameters are invalid and what are expected inline function forma
}
def time_to_epoch(params: List[JValue]): JValue = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
//***
}.getOrElse {
log.error("Cant ...")
JNothing
}
case _ =>
log.error("Number ...")
JNothing
}
For functional programming solution, there's treelog you can use, but it's really not necessary in your case, unless you need the function to be strictly pure.
If time_to_epoch can fail then it is better to return a Try rather than a special failure value like JNothing.
case class MyException(msg: String) extends Exception
def time_to_epoch(params: List[JValue]): Try[JValue] = params match {
case JString(timestamp) :: JString(pattern) :: JString(timezone) :: Nil =>
Try {
???
}
case _ =>
Failure(MyException("Invalid parameters"))
}
You can print the error information using match:
time_to_epoch(???) match {
case Failure(e) =>
println(s"Operation failed: $e")
case _ =>
}
The advantage of this is that it is easy to chain multiple Try operations without having to test for failure at each step. It allows the code to focus on the main code path while still handling errors correctly.
I want to test (in Intellij Idea testing framework) the following code:
def eval(t: Term): Term = t match {
case t if isVal(t) => t
case If(t1,t2,t3) if True == eval(t1) => eval(t2)
case If(t1,t2,t3) if False == eval(t1) => eval(t3)
case Succ(t) if isNum(eval(t)) => Succ(eval(t))
case Pred(t) => eval(t) match {
case Zero => Zero
case Succ(v) if isNum(v) => v
case _ => throw TermIsStuck(Pred(eval(t)))
}
case IsZero(t) => eval(t) match {
case Zero => True
case Succ(v) if isNum(v) =>False
case _ => throw TermIsStuck(IsZero(eval(t)))
}
case _ => throw TermIsStuck(t)
}
For a reference you can see this repository. So I wrote the following test:
test("testEval") {
Arithmetic.term(new Arithmetic.lexical.Scanner("if iszero pred pred 2 then if iszero 0 then true else false else false")) match {
case Arithmetic.Success(res,next) => assert(eval(res) == True)
case Arithmetic.Failure(msg,next) => assert(false)
}
Arithmetic.term(new Arithmetic.lexical.Scanner("pred succ succ succ false")) match {
case Arithmetic.Success(res,next) => assert(Arithmetic.eval(res) == TermIsStuck(Succ(False)))
case Arithmetic.Failure(msg,next) => assert(false)
}
}
Getting the error:
Succ(False)
fos.Arithmetic$TermIsStuck: Succ(False) ...
How can I test this catching the error? Indeed this test should be passed...
You are in Scala you are not supposed to throw exception around, but in scalatest you can do something like this:
a[yourException.type] should be thrownBy yourFunction()
You can do a better version of that,
case class Error(exception: Exception){
throw exception
???
}
Or something like
case object Error
And then you check it like:
Error shouldBe yourFunction()
Looks like you're not using any widespread testing framework for scala (such as scalatest or specs2) - an obvious recommendation would be to use some.
However, if you'd like to stick with "no-framework" approach, one option would be to use good old try .. catch:
val term = Arithmetic.term(
new Arithmetic.lexical.Scanner("pred succ succ succ false")
)
try {
val evaluated = term match {
case Arithmetic.Success(res,next) => Arithmetic.eval(res)
case Arithmetic.Failure(msg,next) => assert(false)
}
} catch {
case TermIsStuck(Succ(False)) => // do nothing, or explicitly mark test as successful
case _ => assert(false) // any other exception should fail the test
}
However, I think the implementation is partially broken - it never returns Arithmetic.Failure, but throws exception. I think the intent of Arithmetic.Failure was exactly to capture such cases. If that's the case, then better implementation would be to replace throw TermIsStuck with Arithmetic.Failure(TermIsStuck(...)).
In a non-functional language, I might do something like:
try {
// some stuff
} catch Exception ex {
return false;
}
// Do more stuff
return true;
In Scala, however, this pattern is clearly not correct. If my scala code looks like this:
try {
// do some stuff
}
catch {
case e: Exception => // I want to get out of here and return false
)
}
// do more stuff
true
How do I properly do that? I don't want to use the "return" statement, of course, but I also don't want to drop through and "do more stuff" and eventually return true.
You want to represent a computation that can either succeed or signal that an error has occurred. That's the perfect use case for the Try monad.
import scala.util.{ Try, Success, Failure }
def myMethod: Try[Something] = Try {
// do stuff
// do more stuff
// if any exception occurs here, it gets wrapped into a Failure(e)
}
So you're returning a Try instead of a Bool, which is infinitely more clear and idiomatic.
Usage example:
myMethod match {
case Success(x) => println(s"computation succeded with result $x")
case Failure(e) => println(s"computation failed with exception $e.getMessage")
}
If you don't even care about the exception, but you just want to return a value in case of success, you can even convert the Try to an Option.
def myMethod: Option[Something] = Try {
// do stuff
// do more stuff
// return something
// if any exception occurs here, it gets wrapped into a Failure(e)
}.toOption
myMethod match {
case Some(x) => println(s"computation succeded with result $x")
case None => println("computation failed")
}
To respond to the question in the comments, you can do
Try {
// do stuff
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
// true
}
although I would suggest to return something more meaningful than a Boolean, whenever it makes sense.
Of course this can be nested
Try {
// do stuff
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
Try {
// something that can throw
} match {
case Failure(_) => false
case Success(_) =>
// do more stuff
true
}
}
but you should consider putting the Try chunks into separate functions (returning a Try).
Ultimately, we can take advantage of the fact that Try is a monad, and do something like this
Try { /* java code */ }.flatMap { _ =>
// do more stuff
Try { /* java code */ }.flatMap { _ =>
// do more stuff
Try { /* java code */ }
}
} match {
case Failure(_) => false // in case any of the Try blocks has thrown an Exception
case Success(_) => true // everything went smooth
}
scala> def f() = try { ??? ; 1 } catch { case _: Throwable => 2 }
f: ()Int
scala> f()
res2: Int = 2
scala> import util._
import util._
scala> def g() = Try { ??? ; 1 } recover { case _ => 2 } get
warning: there was one feature warning; re-run with -feature for details
g: ()Int
scala> g()
res3: Int = 2
HTH. Small functions helps.
Another hint:
scala> def j() = Try (1) map (_ + 42) recover { case _ => 2 } get
warning: there was one feature warning; re-run with -feature for details
j: ()Int
scala> j()
res4: Int = 43
try-catch expression is not good for functional programming.
Anyway, an easy solution that still uses try-catch:
val result = try {
// do some stuff
Some(/* The final expression */)
}
catch {
case e: Exception => // Do something or nothing
None
}
result match {
case Some(r) => // Do something with r
true
case None => false
}
You can use scala.util.Try for cleaner and more functional-styled code.
Refer to https://codereview.stackexchange.com/questions/38689/code-with-many-early-returns-exits-into-the-functional-style
I faced a similar problem like you, but the answer in the Stackexchange CodeReview helped me a lot.
Successful or erroneous cases can be represented by several Scala type.
If you consider the error in the meaning of "something is missing" (e.g. file not found), you can use Option[T] (e.g. Option[File]), with case values None or Some(T). Then orElse, getOrElse or fold and map/flatMap functions can be used to dispatch cased.
You can also use Either[E, T], with (by convention), in Left(E) case the error value (e.g. a String as error message), and in Right(T) successful T value.
Monads Try[T] or Future[T] can be used in the same way.
In the field of I/O, the very nice scala-arm lib provide type ManagedResource[T] that wrap either successful (T) or erroneous (List[Throwable]]) result of computation based on resources.
Valudation types in Scalaz are also useful for such cases.
'util.Try{ do some stuff }.isSuccess'