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))
Related
I have the following function definition:
private val checkSapHealth: IO[ServerHealth] =
BlazeClientBuilder[IO](global).resource.use { client =>
discovery
.senderAddr
.flatMap { addr =>
client
.get(addr.url |+| addr.health) { res =>
res.status match {
case Ok =>
IO(ServerOnline)
case _ =>
IO(ServerOffline)
}
}
.timeout(2.second)
.recover {
case _: Throwable => ServerOffline
}
}
}
I would like to replace the concrete type IO through F[_] to make it more abstract. The problem here is the line:
IO(ServerOnline)
The question is how to make it to
F(ServerOnline)
Try to use cats.effect.Sync
https://typelevel.org/cats-effect/typeclasses/sync.html
So basically using Sync[IO].delay is equivalent to using IO.apply.
private def checkSapHealth[F[_]: Sync]: F[ServerHealth] = ...
use ConcurrentEffect and add an implicit Applicative of F, thus giving you the option to lift values into the F context
private def checkSapHealth[F[_] : ConcurrentEffect](implicit A: Applicative[F]): F[ServerHealth] =
BlazeClientBuilder[F](global).resource.use { client =>
discovery
.senderAddr
.flatMap { addr =>
client
.get(addr.url |+| addr.health) { res =>
res.status match {
case Ok =>
A.pure(ServerOnline)
case _ =>
A.pure(ServerOffline)
}
}
.timeout(2.second)
.recover {
case _: Throwable => ServerOffline
}
}
}
I am having problems understanding the Scala syntax, please advice. I have two snippets of code.
abstract class Try[T] {
def flatMap[U](f: T => Try[U]): Try[U] = this match {
case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
case fail: Failure => fail
}
}
My understanding:
flatMap received as parameter a function f. In turn this function f
receives type parameter T and returns Try of type parameter U.
flatMap ultimately return Try of type parameter U.
Q1 - Is my understanding correct?
Q2 - what is the relation between the return type from f (namely Try[U]) and the return type of flat map Try[U]? Does it have to be the same?
def flatMap[U](f: T => Try[U]): Try[U]
Or can I somehow have something like
def flatMap[U](f: T => Option[U]): Try[U]
In the last snippet of code, I guess that, after I use the function f inside my flatMap, I would need to make the connection between the output of f (namely Option[U]) and the final output demanded by flatMap (I mean Try[U])
EDIT
This code is taken from a scala course. here is the full code (some people asked about it). I just want to understand the syntax.
abstract class Try[T] {
def flatMap[U](f: T => Try[U]): Try[U] = this match {
case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
case fail: Failure => fail
}
def map[U](f: T => U): Try[U] = this match {
case Success(x) => Try(f(x))
case fail: Failure => fail
}
}
Q1 - Is my understanding correct?
It's hard to comment based on your sample code which has method implementation in an abstract class while no concrete classes are defined. Lets consider the following toy version of Try extracted from the Scala API with the flatMap implementation in its concrete classes:
import scala.util.control.NonFatal
sealed abstract class MyTry[+T] {
def flatMap[U](f: T => MyTry[U]): MyTry[U]
}
object MyTry {
def apply[T](r: => T): MyTry[T] =
try MySuccess(r) catch { case NonFatal(e) => MyFailure(e) }
}
final case class MyFailure[+T](exception: Throwable) extends MyTry[T] {
override def flatMap[U](f: T => MyTry[U]): MyTry[U] =
this.asInstanceOf[MyTry[U]]
}
final case class MySuccess[+T](value: T) extends MyTry[T] {
override def flatMap[U](f: T => MyTry[U]): MyTry[U] =
try f(value) catch { case NonFatal(e) => MyFailure(e) }
}
Testing it out with the following function f: T => MyTry[U] where T = String and U = Int, I hope it helps answer your question:
val f: String => MyTry[Int] = s => s match {
case "bad" => MyFailure(new Exception("oops"))
case s => MySuccess(s.length)
}
MyTry("abcde").flatMap(f)
// res1: MyTry[Int] = MySuccess(5)
MyTry("bad").flatMap(f)
// res2: MyTry[Int] = MyFailure(java.lang.Exception: oops)
Q2 - what is the relation between the return type from f (namely Try[U])
and the return type of flat map Try[U]? Does it have to be the same?
In Scala, flatMap is a common method defined in many of Scala containers/collections such as Option[T], List[T], Try[T], Future[T], with a standard signature:
class Container[T] {
def flatMap[U](f: T => Container[U]): Container[U]
}
If you want to have a special map that takes a T => Container1[U] function and returns a Container2[U], it'd probably best not to name it flatMap.
Q1 Largely correct, but just to clarify, all of this happens at compile time - T is not known at runtime (see here)
Q2 Of course you can create a method with signature
...[U](f: T => Option[U]): Try[U]
and you're free to call that method flatMap, but it won't be a standard flatMap:
trait T[A] {
flatMap[B](f: A => T[B]): T[B]
}
There are mathematical reasons for the form of flatMap (which also have implications in Scala's implementation of for expressions). To avoid confusion ...
Rather than altering flatMap's signature, wrap your T => Option[U] with an Option[U] => Try[U] to create a T => Try[U] before passing it to flatMap.
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.
I´ve been playing monad transformer, and I just create one Future[Option]. But after read some blogs there´s something that is not explained and I don't understand.
Here in the implementation of my map and flatMap, in the flatMap once that I get the value of my option, and I apply the function over the value, I have to invoke over the function f(a) the .Value_passed_in_the_case_class(in this case future)
case class FutOpt[A](future: Future[Option[A]]) {
def map[B](f: A => B): FutOpt[B] = {
FutOpt(future.map(option => option.map(value => f(value)))
.recoverWith {
case e: Exception =>
Future.successful(Option.empty)
})
}
def flatMap[B](f: A => FutOpt[B]): FutOpt[B] =
FutOpt(future.flatMap(option => option match {
case Some(a) => f(a).future --> WHAT THIS .future IS DOING?
case None => Future.successful(None)
}))
}
What is happening there?, and how works?.
Regards.
What is happening there?
flatMap expects you to return it a FutOpt[B]:
def flatMap[B](f: A => FutOpt[B]): FutOpt[B]
The signature of your case class requires you to pass in a Future[Option[A]]:
case class FutOpt[A](future: Future[Option[A]])
This line of code:
FutOpt(future.flatMap(option => option match {
case Some(a) => f(a).future
Is constructing a new instance of FutOpt[B], and it needs a value of type Future[Option[B]] in order to be able to construct itself properly. f(a) returns a FutOpt[B], not a Future[Option[B]], and that is why it needs to access .future, which is of type Future[Option[B]].
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) => ...
}