Compiling this code does not result in any warnings about exhaustive pattern matches.
package example
object Hello extends App {
class Problem extends Exception {
override def getCause() : Throwable = new NotImplementedError("I'm not done")
}
handle(new Problem)
def handle(ex: Exception): Unit = {
println("handler called: %s".format(ex))
val result = ex.getCause match {
case null => println("null")
case e : Exception => println("except")
// no warning about the match not being exhaustive
// case t : Throwable => println("throw")
}
}
}
When I run the code I get this stack trace:
scala.MatchError: scala.NotImplementedError: I'm not done (of class scala.NotImplementedError)
at example.Hello$.handle(Hello.scala:13)
at example.Hello$.delayedEndpoint$example$Hello$1(Hello.scala:9)
at example.Hello$delayedInit$body.apply(Hello.scala:3)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main$1(App.scala:76)
at scala.App.$anonfun$main$1$adapted(App.scala:76)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:563)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:561)
at scala.collection.AbstractIterable.foreach(Iterable.scala:926)
at scala.App.main(App.scala:76)
at scala.App.main$(App.scala:74)
at example.Hello$.main(Hello.scala:3)
at example.Hello.main(Hello.scala)
If I uncomment out the line case t : Throwable => println("throw"), the program prints throw.
Why isn't a warning printed to the console when I compile this code? Thanks!
Related
I have a method with the following definition :
def userPosts(userId: String): Future[List[Post]]
I would like to have a method which returns cached date or calls userPosts if there is no cached data. I've prepared something like this:
def cachedUserPosts(userId: String): Future[List[Post]] = {
for {
cached <- bucket.get[List[Post]]("posts_" + userId)
} yield {
cached match {
case Some(list) => list
case None => userPosts(userId) match {
case list:List[Post] => list
}
}
}
}
I have an error:
[error] play - Cannot invoke the action, eventually got an error: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)
play.api.Application$$anon$1: Execution exception[[MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.6.jar:2.3.6]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.6.jar:2.3.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
at scala.Option.map(Option.scala:145) [scala-library-2.11.2.jar:na]
Caused by: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)
at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:130) ~[classes/:na]
at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:126) ~[classes/:na]
at scala.util.Success$$anonfun$map$1.apply(Try.scala:236) ~[scala-library-2.11.2.jar:na]
at scala.util.Try$.apply(Try.scala:191) ~[scala-library-2.11.2.jar:na]
at scala.util.Success.map(Try.scala:236) ~[scala-library-2.11.2.jar:na]
2015-07-26 19:33:24.211 INFO net.spy.memcached.auth.AuthThread: Authenticated to 192.168.10.42/192.168.10.42:11210
Do you have any idea what's going on?
You are pattern matching on a Future, if we try an analogous pattern match in the console :
val p = Promise[Int]
val f = p.future
p.success(5) // fulfill the promise
Pattern matching on f gives us :
scala> f match { case i: Int => i }
<console>:15: error: pattern type is incompatible with expected type;
found : Int
required: scala.concurrent.Future[Int]
f match { case i: Int => i }
^
Your userPosts function returns a Future[List[Post]] and you can't pattern match on the future directly to get the List[Post]. You can find more information on working with Future on the Scala website.
Lets fix your cachedUserPosts function. Your version could also be written as (does not compile):
bucket.get[List[Post]]("posts_" + userId) map ( cached => cached match {
// List[Post] in Future.map => Future[List[Post]]
case Some(list) => list
// userPosts already gives us a Future[List[Post]]
// We could have used Await.result(userPosts(userId), 5.seconds)
// to get the List[Post] from the Future, but then we would be blocking
case None => userPosts(userId)
})
If we convert the map into a flatMap we can return a Future[List[Post]], but then we would also need to return a Future[List[Post]] in the Some case, which can easily be done using Future.successful :
bucket.get[List[Post]]("posts_" + userId) flatMap ( cached => cached match {
// List[Post] to Future[List[Post]] using Future.successful
case Some(list) => Future.successful(list)
case None => userPosts(userId)
})
We could clean this up a little bit, by using map/getOrElse instead of pattern matching on the Option :
def cachedUserPosts(userId: String): Future[List[Post]] =
bucket.get[List[Post]]("posts_" + userId).flatMap { cached =>
cached.map(list => Future.successful(list))
.getOrElse(userPosts(userId))
}
I am using some client library and had some code that ignored a specific exception using scala.util.control.Exception.ignoring:
ignoring(classOf[ReallyNotThatExceptionalException]) {
stuff()
}
Now that library changed to wrap all exceptionn in another exception class, which forced me to change my code to this:
try { stuff() }
catch {
case e:WrapperException if e.getCause != null && e.getCause.isInstanceOf[ReallyNotThatExceptionalException] => { }
}
So what I'm looking for a more readable way to express "catch exceptions that are caused by".
0__'s answer is good, but it would be better if you were not forced to write a specific object (CausedByFoo) for each potential exception.
As it happens, there is not much to change to end up with a generic CausedBy helper object:
class Foo(message: String) extends Exception(message)
class Bar(cause: Throwable) extends Exception(cause)
object CausedBy {
def unapply(e: Throwable): Option[Throwable] = Option(e.getCause)
}
def test(block: => Unit): String =
try {
block
"ok"
} catch {
case CausedBy(ex: Foo) => "not ok: " + ex.getMessage
}
test(println("hello"))
test(println("hello".toInt)) // uncaught exception
test(throw new Bar(new Foo("Ooops, foo error!"))) // caught
As should be obvious, you can use CausedBy with any exception (by example by doing case CausedBy(ex: Baz).
You can even nest it to handle an exception caused by an exception caused by an exception (by doing something like case CausedBy(CausedBy(ex: Foo))
The catch block can handle any regular pattern match, so
class Foo extends Exception
class Bar(cause: Exception) extends Exception(cause)
object CausedByFoo {
def unapply(e: Exception): Boolean = e.getCause match {
case _: Foo => true
case _ => false
}
}
def test(block: => Unit): String =
try {
block
"ok"
} catch {
case CausedByFoo() => "not ok"
}
test(println("hello"))
test(println("hello".toInt)) // uncaught exception
test(throw new Bar(new Foo)) // caught
I tweaked RĂ©gis' answer to allow matching against both outer and inner exception type.
trait CausedBy[T <: Throwable] {
def unapply(e: T): Option[Throwable] = Option(e.getCause)
}
object IOExceptionCausedBy extends CausedBy[IOException]
Then you can match like this:
try {
// do some DB thing
} catch {
case IOExceptionCausedBy(exception: PSQLException) =>
// handle PSQL exception
}
When using some code like the following:
scala> Future { null } onComplete { case Success(v) => v.toString }
Scala throws the following exception:
scala> java.lang.NullPointerException
at $line14.$read$$iw$$iw$$anonfun$2.apply(<console>:11)
at $line14.$read$$iw$$iw$$anonfun$2.apply(<console>:11)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
This would be OK, since I am not handling any exceptions. The problem is that my application hangs completely.
I am using concurrent.ExecutionContext.Implicits.global and I think onComplete is executed in this global execution context. The problem is that it seems like the execution context stops accepting any work and and the application just hangs.
Do I have to explicitly use try ... catch so that I protect my app in case something unexpected happens in onComplete?
Thank you
IIRC, this was an issue only in the very earliest implementation.
You can supply a handler or "reporter":
scala> import util._
import util._
scala> import concurrent._
import concurrent._
scala> ExecutionContext.fromExecutor(null, (t: Throwable) => println(s"Hi, $t"))
res0: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl#221a3fa4
scala> implicit val x = res0
x: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl#221a3fa4
scala> Future { null } onComplete { case Success(v) => v.toString }
<console>:16: warning: match may not be exhaustive.
It would fail on the following input: Failure(_)
Future { null } onComplete { case Success(v) => v.toString }
^
Hi, java.lang.NullPointerException
scala>
Everything is handled.
First of all, the NullPointerException you get has nothing to do with the future; it does not happen inside the Future block.
What you can do, is wrapping code that might return null in Option().
Your code would then look like this:
Future { Option(mightBeANull) } onComplete { case Success(v) => v.map(_.toString) }
The scala test code:
import play.api.test._
import scala._
import org.specs2.execute.Result
object ThrowTest extends PlaySpecification {
"throwA" should {
"catch the exception test1" in {
world must throwA[Exception]
}
"catch the exception test2" in {
hello {
world =>
world must throwA[Exception]
}
}
}
def hello(action: (String) => Result) = {
action(world)
}
def world: String = {
throw new Exception("world-exception")
}
}
Why test1 is working as I expected, but test2 is not, which throws the exception to outer and never catch it:
[info] ! catch the exception test2
[error] Exception: world-exception (ThrowTest.scala:26)
[error] database.ThrowTest$.world(ThrowTest.scala:26)
[error] database.ThrowTest$.hello(ThrowTest.scala:22)
[error] database.ThrowTest$$anonfun$1$$anonfun$apply$4.apply(ThrowTest.scala:14)
[error] database.ThrowTest$$anonfun$1$$anonfun$apply$4.apply(ThrowTest.scala:14)
[info] Total for specification ThrowTest
Because for test 2 your exception is thrown from hello before calling action. action is a String => Result and you call it with world which - when evaluated - throws an exception, therefor, all this code:
world =>world must throwA[Exception]
is never executed.
This is a simple question that I cant just figure out:
The following code gives the following compilation error:
def parseJson(q: String) = Option[JsValue]{
try{
Json.parse(q)
}catch{
case e: com.codahale.jerkson.ParsingException => None
}
}
Error
[error] found : None.type (with underlying type object None)
[error] required: play.api.libs.json.JsValue
[error] case e: com.codahale.jerkson.ParsingException => None
Why cant I return None considering my response type is Option[JsValue]?
You actually want to put Json.parse(q) into Some() and not wrapping the whole code in an Option[JsValue] but using it as signature:
def parseJson(q: String): Option[JsValue] = {
try{
Some(Json.parse(q))
}catch{
case e: com.codahale.jerkson.ParsingException => None
}
}
But anyway you should prefer using scala.util.control.Exception:
import scala.util.control.Exception._
catching(classOf[com.codahale.jerkson.ParsingException]).opt(Json.parse(q))
I think you just got the syntax for the method definition wrong. The way you wrote it you are calling Option.apply on the result of the try/catch block. I think you meant to write this:
def parseJson(q: String): Option[JsValue] = {
try {
Some(Json.parse(q))
} catch {
case e: com.codahale.jerkson.ParsingException => None
}
}