Im writing my own class Success or Error. I want to create a way to build SuccessOrError from Try and then use Try instead of ugly try-catch in flatMap. But I'm stuck at this point. How should I best write a function fromTry in order to be able to write like this?
case SuccessOrError.Success(v) ⇒ SuccessOrError.fromTry(Try(f(v)))
enum SuccessOrError[+V]:
case Success(x: V) extends SuccessOrError[V]
case Error(e : Throwable) extends SuccessOrError[V]
def flatMap[Q](f: V ⇒ SuccessOrError[Q]): SuccessOrError[Q] =
this match
case SuccessOrError.Success(v) ⇒ try f(v) catch case NonFatal(e) => SuccessOrError.Error(e)
case SuccessOrError.Error(e) ⇒ SuccessOrError.Error(e)
object SuccessOrError:
def fromTry[Q](f: Try[Q]): SuccessOrError[Q] = ???
Converting from Try to another error model is quite a common practice. Perhaps consider how they approach it in scala-cats library as inspiration
Validated
/**
* Converts a `Try[A]` to a `Validated[Throwable, A]`.
*/
def fromTry[A](t: Try[A]): Validated[Throwable, A] =
t match {
case Failure(e) => invalid(e)
case Success(v) => valid(v)
}
or Either
/**
* Converts a `Try[A]` to a `Either[Throwable, A]`.
*/
def fromTry[A](t: Try[A]): Either[Throwable, A] =
t match {
case Failure(e) => left(e)
case Success(v) => right(v)
}
or ApplicativeError
/**
* If the error type is Throwable, we can convert from a scala.util.Try
*/
def fromTry[A](t: Try[A])(implicit ev: Throwable <:< E): F[A] =
t match {
case Success(a) => pure(a)
case Failure(e) => raiseError(e)
}
Note the pattern (bad pun, applogies) of simply pattern matching on happy and unhappy cases, so we can try emulating it
def fromTry[Q](f: Try[Q]): SuccessOrError[Q] =
f match {
case scala.util.Failure(e) => SuccessOrError.Error(e)
case scala.util.Success(v) => SuccessOrError.Success(v)
}
Related
I am trying to write an toFirstOrSecond method that will allow me to call Try without any extra code.
object A:
enum FirstOrSecond[+V]:
case First(x: V) extends FirstOrSecond[V]
case Second(e : Throwable) extends FirstOrSecond[V]
def map[Q](f: V ⇒ Q): FirstOrSecond[Q] =
this match
case FirstOrSecond.Second(e) ⇒ FirstOrSecond.Second(e)
case FirstOrSecond.First(v) ⇒ Try { FirstOrSecond.First(f(v)) }.toFirstOrSecond match
case FirstOrSecond.First(m) => FirstOrSecond.First(f(v))
case FirstOrSecond.Second(m) => FirstOrSecond.Second(m)
object Try:
def toFirstOrSecond[Q](f: Try[Q]): FirstOrSecond[Q] =
f match
case Failure(e) => FirstOrSecond.First(e)
case Success(v) => FirstOrSecond.Second(v)
But I get an error object Try in object A does not take parameters How do i solve this? Thanks
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´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 Future[T] and I want to map the result, on both success and failure.
Eg, something like
val future = ... // Future[T]
val mapped = future.mapAll {
case Success(a) => "OK"
case Failure(e) => "KO"
}
If I use map or flatmap, it will only map successes futures. If I use recover, it will only map failed futures. onComplete executes a callback but does not return a modified future. Transform will work, but takes 2 functions rather than a partial function, so is a bit uglier.
I know I could make a new Promise, and complete that with onComplete or onSuccess/onFailure, but I was hoping there was something I was missing that would allow me to do the above with a single PF.
Edit 2017-09-18: As of Scala 2.12, there is a transform method that takes a Try[T] => Try[S]. So you can write
val future = ... // Future[T]
val mapped = future.transform {
case Success(_) => Success("OK")
case Failure(_) => Success("KO")
}
For 2.11.x, the below still applies:
AFAIK, you can't do this directly with a single PF. And transform transforms Throwable => Throwable, so that won't help you either. The closest you can get out of the box:
val mapped: Future[String] = future.map(_ => "OK").recover{case _ => "KO"}
That said, implementing your mapAll is trivial:
implicit class RichFuture[T](f: Future[T]) {
def mapAll[U](pf: PartialFunction[Try[T], U]): Future[U] = {
val p = Promise[U]()
f.onComplete(r => p.complete(Try(pf(r))))
p.future
}
}
Since Scala 2.12 you can use transform to map both cases:
future.transform {
case Success(_) => Try("OK")
case Failure(_) => Try("KO")
}
You also have transformWith if you prefer to use a Future instead of a Try. Check the documentation for details.
In a first step, you could do something like:
import scala.util.{Try,Success,Failure}
val g = future.map( Success(_):Try[T] ).recover{
case t => Failure(t)
}.map {
case Success(s) => ...
case Failure(t) => ...
}
where T is the type of the future result. Then you can use an implicit conversion to add this structure the Future trait as a new method:
implicit class MyRichFuture[T]( fut: Future[T] ) {
def mapAll[U]( f: PartialFunction[Try[T],U] )( implicit ec: ExecutionContext ): Future[U] =
fut.map( Success(_):Try[T] ).recover{
case t => Failure(t)
}.map( f )
}
which implements the syntax your are looking for:
val future = Future{ 2 / 0 }
future.mapAll {
case Success(i) => i + 0.5
case Failure(_) => 0.0
}
Both map and flatMap variants:
implicit class FutureExtensions[T](f: Future[T]) {
def mapAll[Target](m: Try[T] => Target)(implicit ec: ExecutionContext): Future[Target] = {
val promise = Promise[Target]()
f.onComplete { r => promise success m(r) }(ec)
promise.future
}
def flatMapAll[Target](m: Try[T] => Future[Target])(implicit ec: ExecutionContext): Future[Target] = {
val promise = Promise[Target]()
f.onComplete { r => m(r).onComplete { z => promise complete z }(ec) }(ec)
promise.future
}
}
I want to flatten a Try[Option[T]] into a Try[T]
Here is my code
def flattenTry[T](t: Try[Option[T]]) : Try[T] = {
t match {
case f : Failure[T] => f.asInstanceOf[Failure[T]]
case Success(e) =>
e match {
case None => Failure[T](new Exception("Parsing error"))
case Some(s) => Success(s)
}
}
}
Is there a better way ?
You could try something like this which is a little neater:
val t = Try(Some(1))
val tt = t.flatMap{
case Some(i) => Success(i)
case None => Failure(new Exception("parsing error"))
}
More generically, this would be:
def flattenTry[T](t: Try[Option[T]]) : Try[T] = {
t.flatMap{
case Some(s) => Success(s)
case None => Failure(new Exception("parsing error"))
}
}
The trick is to convert the Option into Try for using flatmap.
Using getOrElse, you could also write:
def flattenTry[T](t: Try[Option[T]]): Try[T] = {
t flatMap { o => o map (Success(_)) getOrElse Failure(new Exception("parsing error")) }
}
You could make this even shorter, but possibly at the cost of functional purity (throwing is a side-effect) and performance (due to throwing/catching):
def flattenTry[T](t: Try[Option[T]]): Try[T] = {
Try(t.get getOrElse { throw new Exception("parsing error") })
}
One possible way is to use Try(o.get) to convert o: Option to o: Try.
This is very concise, but throws and catches exception when handling None, which might perhaps be a performance concern sometimes (or sometimes an ideological / code rules concern). Still, the code is so concise and well readable I think it is worth mentioning:
def flattenTry[T](t: Try[Option[T]]) : Try[T] = {
t.flatMap(o => Try(o.get))
}
Note: not that I recommend using it, but for completeness: even a shorter version is possible, which throws / catches even when Try is a failure:
def flattenTry[T](t: Try[Option[T]]) : Try[T] = {
Try(t.get.get)
}