Matching with andThen - scala

My (incomplete) code looks like this:
def doSomething(name: String): { implicit request =>
check(name).map {
case Something() => Ok()
case SomethingElse() => NoContent
}
.andThen {
case r: Result => logger.info(s"Request finished with status ${r.header.status}")
}
}
I get an error like this:
[error] myfile.scala:39: fruitless type test: a value of type scala.util.Try[play.api.mvc.Result] cannot also be a play.api.mvc.Result
[error] case r: Result => logger.info(s"Request finished with status ${r.header.status}")
How can I match that Try?

If you're trying to match on a Try, you need to match on its constructors, not what its constructors hold:
.andThen {
case Success(r) =>
logger.info(s"Request finished with status ${r.header.status}")
case Failure(e) => // Handle failure
}

Related

Scala Found Unit Expected Future[Result]

def update() = AuthAction.async(parse.json) { implicit request =>
val list = request.body.asInstanceOf[JsArray].value
list.foreach( mapping => {
repository.update()
}.andThen {
case Success(value) => repository.update2()
case Failure(exception) => {
BadRequest(errorResponse(Json.toJson(""), "updation failed"))
}
})
}
I have a controller function where I want to wait for a DB repo function ( repository.update2() )to complete and then send a response, but its saying "Found Unit expected Future[Result]"
Your success claus is presumably returning Unit where a Future response is required. Try this:
.andThen {
case Success(value) =>
repository.update2()
Future.successful(Ok("repository updated"))
case Failure(exception) =>
Future.successful(BadRequest(exception.getMessage())))
}

Why do I need an extra asInstanceOf

I wrote this code
listOfClassNames.map{ className =>
Try {
GuiceInjector.getInstance(Class.forName(className)).asInstanceOf[BaseClass]
} recover {
case _ => Option.empty[(String, BaseClass)]
} match {
case Success(bc) => Some((className, bc))
case _ => Option.empty[(String, BaseClass)]
}
}
The above code throws an error
type mismatch;
[error] found : List[Option[(String, Object)]]
[error] required: List[Option[(String, BaseClass)]]
Now if I change the code to
listOfClassNames.map{ className =>
Try {
GuiceInjector.getInstance(Class.forName(className)).asInstanceOf[BaseClass]
} recover {
case _ => Option.empty[(String, BaseClass)]
} match {
case Success(bc) => Some((className, bc.asInstanceOf[BaseClass]))
case _ => Option.empty[(String, BaseClass)]
}
}
Now it works. but according to me the second asInstanceOf is unnecessary because the first object itself was typecast. no?
You have to remove the call to recover. It's not needed anyway, because you process the error case inside match, but it messes up the types and correctness of your code.
Consider the types: Try { /* ... */.asInstanceOf[BaseClass] }
has type Try[BaseClass].
Then Try { /* ... */ } recover { case _ => Option.empty[(String, BaseClass)] } is a Try of the common supertype of BaseClass and Option[(String, BaseClass)], so it's Try[AnyRef].
Thus inside the match in case Success(bc) => this bc has type AnyRef, and at runtime it can be either an instance of BaseClass or None. If the original fetch of BaseClass from Guice fails, this bc is None, and you get a ClassCastException with the additional isInstanceOf.

Future does not compile with Success and Failure

Why this does not compile? I get the following error in the Success and Failure lines:
constructor cannot be instantiated to expected type; found :
scala.util.Success[T] required: Int
And the code:
val future = Future { 1 }
future.map {
case Success(s) => s
case Failure(f) => 0
}
Because map of a Future[Int] expects a function with domain Int, but instead you are trying to pass some function with domain Try[Int] into it.
The syntactically closest thing that works is onComplete:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
val future = Future { 1 }
future.onComplete {
case Success(s) => s
case Failure(f) => 0
}
However, this is probably not what you want, because the expressions s and 0 don't do anything, they are therefore not very interesting callbacks. Instead, you probably either want to transform the result:
future.transform {
case Success(s) => Success(s)
case Failure(f) => Success(0)
}
or recover from the failure with a 0:
future.recover {
case f: Exception => 0
}
Are you trying to do something like:
val future = Future {
1
}
future onComplete {
case Success(s) => s
case Failure(f) => 0
}

Generic Try[T] function

I want to refactor some common error handling logic in a generic Try[T] handler, similar to this:
def handler[T](t: Try[T], successFunc: T => Unit) = {
t.map {
case Success(res) => { // type mismatch required T, found Any (in successFunc line)
//case Success(res: T) => { // Type abstract type pattern T is unchecked since it is eliminated by erasure
successFunc(res)
}
case Failure(e: CustomException) => {
// custom actions
}
case Failure(e) => {
// custom actions
}
}
}
Seems I can't match against the type T because of type erasure. But I can't pass an Any to successFunc.
How can I implement this function?
Mapping on a try applies a function to the value held by a success of that try, what you have there is not a Success or a Failure, it's a T, what you want is a match:
def handler[T](t: Try[T], successFunc: T => Unit) = {
t match {
case Success(res) =>
successFunc(res)
case Failure(e: FileNotFoundException) =>
case Failure(e) =>
}
}
The usage in your case of map would be:
t.map(someT => successFunc(someT))

Elegant Handling of Scala Future[Either]]

I have a type whose shape is like this:
val myType: Future[Either[MyError, TypeA]] = // some value
I know that I could pattern match on this and get to the Right or Left type, but the problem is that I would have to nest my pattern matching logic. I'm looking for much more elegant way of handling this? Any suggestions?
If you encode your MyError as an exception, you don't need the Either anymore and can simply patternMatch against the completion, or use a recoverWith to map it to another type:
myType.onComplete {
case Success(t) =>
case Failure(e) =>
}
To map your existing Either types you could do something like this:
case class MyException(e: MyError) extends Exception
def eitherToException[A](f: Future[Either[MyError,A]]): Future[A] = {
f.flatMap {
case Left(e) => Future.failed(MyException(e))
case Right(x) => Future.successful(x)
}
}
val myType2 = eitherToException(myType)
Alternatively, if MyError and TypeA are under your control, you could create a common super type and pattern match against that:
sealed trait MyResult
final case class MyError() extends MyResult
final case class TypeA() extends MyResult
myType.map {
case MyError() => ...
case TypeA() => ...
}
You can create custom extractor objects:
object FullSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Right(x)) => Some(x)
case _ => None
}
}
object PartSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Left(err)) => Some(err)
case _ => None
}
}
And
val myType: Future[Either[MyError, TypeA]] = // some value
myType.onComplete {
case FullSuccess(x) => ... // equivalent to case Success(Right(x))
case PartSuccess(x) => ... // equivalent to case Success(Left(x))
case Failure(e) => ...
}