Imagine I have OptionT[IO, Value] like this
case class FailureMsg(code: String, ex: Option[Throwable])
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
.map { value ⇒
println("Mapping over")
value
}
.flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
.toRight(FailureMsg("Code0", None))
.recoverWith {
case ex ⇒ // Not Throwable!
EitherT.leftT[IO, Int](FailureMsg("Code1", Some(ex)))
}
.value
How can I catch err1 and wrap it into Left[FailureMsg]. I expected recoverWith help me but surprisingly it's alias of mapLeft. What should I do ?
I wrote helper class to do this.
implicit class EitherTExt[F[_], A, B](val obj: EitherT[F, A, B]) {
def recoverThrowable(pf: PartialFunction[Throwable, Either[A, B]])(implicit A: ApplicativeError[F, Throwable]): EitherT[F, A, B] =
EitherT(obj.value.recover(pf))
}
Let me know if there is more elegant shorter way.
I would follow the types.
val start: OptionT[IO, Int] = OptionT.some[IO](12345)
val thenMap: OptionT[IO, Int] = start.map { value ⇒
println("Mapping over")
value
}
// here it will get off the rails
val thenFlatMapF: OptionT[IO, Int] =
thenMap.flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
val thenToRight: EitherT[IO, FailureMsg, Int] =
thenFlatMapF.toRight(FailureMsg("Code0", None))
val result: IO[Either[FailureMsg, Int]] = thenToRight.value
thenFlatMapF won't produce OptionT[IO, Int] if IO.raiseError is the case, because no default mapping of Throwable to what? And you will get exception in folding result of IO.raiseError.
First attempt to fix it, will illustrate it:
val thenFlatMapF: OptionT[IO, Int] = thenMap.flatMapF[Int](_ ⇒ {
IO.raiseError[Option[Int]](new RuntimeException("err1")).recoverWith {
case err =>
val result: Option[Int] = ???
IO.pure(result)
}
})
How to handle error in place without breaking IO and return Option so that OptionT[IO, Int] is produced?
So basically, in this case, if you expect flatMapF to fail and need information on error, then it is better to have EitherT as its container, rather than OptionT.
Once done, possible solution shows that at some point leftMap or variance should be done to map the Throwable to FailureMsg. One of reasons is because IO has default error expressed as Throwable. One can't just mix FailureMsg and Throwable. Either inheritance is required so that FailureMsg is of type Throwable/Exception, or mapping should be done of errors in suitable places.
My rough solution would be:
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
// ok, let's do some input interpretation
.map { value ⇒
println("Mapping over")
value
}
// if no input, means error
.toRight(FailureMsg("Code0", None))
// if there is interpreted input to process, do it and map the errors
.flatMapF(_ ⇒ IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
// just for illustration, that if you have temptation to map on error,
// most likely there won't be only exception
case t: Throwable => FailureMsg("unexpected", Some(t))
}))
.value
However, normally contents of flatMapF would be a separate function or effect that would include error handling. Something like this:
val doJob: Int => IO[Either[FailureMsg, Int]] = arg =>
IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
case t: Throwable => FailureMsg("unexpected", Some(t))
})
val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
.map { value ⇒
println("Mapping over")
value
}
.toRight(FailureMsg("Code0", None))
.flatMapF(arg ⇒ doJob(arg))
.value
Related
I have an iterable of arrays that I am trying to turn into case classes, and I'm mapping over them to do so. In the event of an array being non-convertable to a case class, I want to log a warning and proceed with the mapping. However, when I implement the warning, the return type changes from Iterable[MyCaseClass] to Iterable[Any] which is not what I want. E.g.:
case class MyCaseClass(s1: String, s2: String)
object MyCaseClass {
def apply(sa: Array[String]) = new MyCaseClass(sa(0), sa(1))
}
val arrayIterable: Iterable[Array[String]] = Iterable(Array("a", "b"), Array("a", "b", "c"))
def badReturnType(): Iterable[Any] = { // Iterable[Any] is undesireable
arrayIterable map {
case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
case _ => println("something bad happened!") // but warnings are good
}
}
def desiredReturnType(): Iterable[MyCaseClass] = { // Iterable[MyCaseClass] is desireable
arrayIterable map {
case sa: Array[String] if sa.length == 2 => MyCaseClass(sa)
// but no warnings if things go wrong!
}
}
I want to write a function that meets the following criteria:
maps over the Iterable, converting each element to a MyCaseClass
log warnings when I get an array that cant be converted to a MyCaseClass
after logging the warning, the array passed into the match criteria is ignored/discarded
the return type should be Iterable[MyCaseClass].
How can I meet these conditions?
Consider using List instead of Array, and try wrapping in Option in combination with flatMap
l flatMap {
case e if e.length == 2 => Some(MyCaseClass(e))
case e => println(s"$e is wrong length"); None
}
Another approach is partitionMap
val (lefts, rights) = l.partitionMap {
case e if e.size == 2 => Right(MyCaseClass(e))
case e => Left(s"$e is wrong length")
}
lefts.foreach(println)
rights
You can do something like this:
final case class MyCaseClass(s1: String, s2: String)
def parse(input: Array[String]): Either[String, MyCaseClass] = input match {
case Array(s1, s2) => Right(MyCaseClass(s1, s2))
case _ => Left(s"Bad input: ${input.mkString("[", ", ", "]")}")
}
def logErrors(validated: Either[String, _]): Unit = validated match {
case Left(error) => println(error)
case Right(_) => ()
}
def validateData(data: IterableOnce[Array[String]]): List[MyCaseClass] =
data
.iterator
.map(parse)
.tapEach(logErrors)
.collect {
case Right(value) => value
}.toList
Which you can use like this:
val arrayIterable = Iterable(Array("a", "b"), Array("a", "b", "c"))
validateData(arrayIterable)
// Bad input: [a, b, c]
// res14: List[MyCaseClass] = List(MyCaseClass("a", "b"))
I have a function like this:
def foo(item: Item) : Option[Int] = Try{
// Some code that can blow up
}.toOption
I have a list of items and I want to map through them, and apply the above function. But if the function above blows up and returns a None then the result of the map should be an error:
items.map{
item => foo(item)
}
Is map not the right thing to do here? It doesn't seem like it
This is called traverse. If you can use cats, it is as simple as:
import cats.implicits._
val result = items.traverse(foo) // Option[List[Int]]
If not, you can implement it pretty easily:
def traverse[A, B](data: List[A])(f: A => Option[B]): Option[List[B]] = {
#annotation.tailrec
def loop(remaining: List[A], acc: List[B]): Option[List[B]] =
remaining match {
case a :: as => f(a) match {
case Some(b) => loop(remaining = as, b :: acc)
case None => None
}
case Nil => Some(acc.reverse)
}
loop(remaining = data, acc = List.empty)
}
Which you can use like:
val result = traverse(items)(foo) // Option[List[Int]]
(however, I would suggest you to use cats instead, since it is more general).
For out-of-the-box short-circuiting, consider wrapping the list-mapping with Try like so
def fooUnsafe(item: Item): Int = // might throw
Try(items.map(fooUnsafe))
If you wish to keep def foo(item: Item) : Option[Int] signature then the following will also short-circuit
Try(list.map(v => foo(v).get))
I want to apply a sequence of transformations to a String but stopping when there is an error. This is an example of a more general pattern (a kind of Chain of Responsibility pattern or Visitor pattern)
If it is possible, I want to avoid using Cats or Scalaz at the moment. If you know how to do it on plain Scala and also Cats/Scalaz I will happy to see the code in the answer ;)
So following is my approach (assertions at the end of the code), but It is not stopping when an error is found. Basically is skipping the execution of the transformation X times.
type Error = String
sealed trait Transformer {
def transform(txt:String) : Either[Error, String]
}
object Transformer1 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_One")
}
object Transformer2 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Two")
}
object Transformer3 extends Transformer {
override def transform(txt: String): Either[Error, String] = Right(s"${txt}_Three")
}
object TransformerError extends Transformer {
override def transform(txt: String): Either[Error, String] = Left("Error!!!!")
}
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] =
transformers.foldLeft(Right(txt):Either[Error, String])( (result, t) => result match {
case Right(txt) => t.transform(txt)
case error => error
} )
val tOk = Seq(Transformer1, Transformer2, Transformer3)
val tError = Seq(Transformer1, TransformerError, Transformer3)
assert(transform("Whatever", tOk) == Right("Whatever_One_Two_Three"))
assert(transform("Whatever", tError) == Left("Error!!!!"))
Any suggestion?
Thanks!!
In Scala 2.12, Either is right-biased, so for-yield would do the trick.
for {
v1 <- Transformer1.transform("Whatever")
v2 <- Transformer2.transform(v1)
v3 <- Transformer3.transform(v2)
} yield {
v3
}
evaluates to Right(Whatever_One_Two_Three), while
for {
v1 <- Transformer1.transform("Whatever")
v2 <- TransformerError.transform(v1)
v3 <- Transformer3.transform(v2)
} yield {
v3
}
evaluates to Left(Error!!!!)
However, if you would like to return a result with all the transformations applied until an error was reached, that is,
assert(transform("Whatever", tError) == Right("Whatever_One"))
then the following refactoring of transform function might work:
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] = {
type Current = Either[Error, String]
type Previous = Either[Error, String]
def foldLeftWithEarlyReturn: Tuple2[Current, Previous] = {
transformers.foldLeft[Tuple2[Current, Previous]](Right(txt) , Right(txt)){
(result, t) => result match {
case ( Right(txt) , Right(previousTxt) ) => ( t.transform(txt) , Right(txt) )
case ( Left(error) , Right(previousTxt) ) => return ( Right(previousTxt), Left(error) )
case e => e
}
}
}
if (foldLeftWithEarlyReturn._1.isLeft)
foldLeftWithEarlyReturn._2 // this means last transformation in sequence resulted in Left, so return previous
else
foldLeftWithEarlyReturn._1
}
When processing a collection, if you want early termination you often have to turn to recursion.
def transform(txt :String
,transformers :Seq[Transformer]
): Either[Error, String] = transformers match {
case Seq() => Right(txt)
case hd +: tl => hd.transform(txt).fold(Left(_), transform(_, tl))
}
A tail-recursive version is also possible, if a little less concise.
#tailrec
def transform(txt :String
,transformers :Seq[Transformer]
): Either[Error, String] = transformers match {
case Seq() => Right(txt)
case hd +: tl =>
val rslt = hd.transform(txt)
if (rslt.isLeft) rslt else transform(rslt.toSeq.head, tl)
}
Pure Scala
Probably, the simplest way to make your code short-circuiting, is to use return statement. It returns the result from the innermost named function it's contained in:
def transform(txt: String, transformers: Seq[Transformer]): Either[Error, String] =
transformers.foldLeft(Right(txt):Either[Error, String])( (result, t) =>
result match {
case Right(txt) => t.transform(txt)
case error => return error
} )
So the return error statement in this code would immediately return the first Left encountered from the transform function.
Cats
In cats you don't really have to do anything special. It short-circuits certain calls for some monads automatically, because monads have to implement tailRecM, and some monads (including Either) implement it in a lazy way to avoid doing useless flatMaps.
import cats.implicits._
def transformCats(txt: String, transformers: List[Transformer]): Either[Error, String] = {
// It seems this is needed to help Scala with type inference.
type Result[T] = Either[Error, T]
// foldLeftM is implemented in terms of tailRecM,
// and thus is short-circuiting for Either
transformers.foldLeftM(txt)((result, tf) => tf.transform(result): Result[String])
}
Given a block that catches more than one exception is it possible to handle multiple exceptions without putting the desired value in every case block? e.g. it would be nice if something like this worked:
val foo: Int = try {
//do stuff that results in an Int
} catch {
case e: SomeException => //do something if this gets thrown
case e: SomeOtherException => //do something different if this gets thrown
0
}
But that results in a compile error (type mismatch; found : Unit required: Int). I could put the default in each throwable case e: SomeException => {/*do something if this gets thrown*/; 0} - but that just seems like code smell so I'm hoping there is a more elegant solution.
You could simply wrap the exception handling:
val foo: Int = try {
//do stuff that results in an Int
17
} catch { case t: Throwable => t match {
case e: SomeException => //do something if this gets thrown
case e: SomeOtherException => //do something different if this gets thrown
}
42
}
You can take advantage of partial functions to do your error handling using the built in Try
val foo: Int ={
val value = Try{
//stuff
}
unwrap(0, value){
case x: SomeException => doStuff()
case x: OtherExcetion => doMoreStuff()
}
}
def unwrap[A](ret: A, value: Try[A])(f: Failure[A] => Unit): A = value match{
case Success(x) => x
case x: Failure => f(x); ret
}
and voila, you've handled it quite well.
The catch keyword expects a PartialFunction, which can easily be chained with andThen:
scala> val pf1: PartialFunction[Throwable, Unit] = { case _: IllegalArgumentException => println("pf1") }
pf1: PartialFunction[Throwable,Unit] = <function1>
scala> val pf2: PartialFunction[Unit, Int] = { case _ => println("pf2"); 0}
pf2: PartialFunction[Unit,Int] = <function1>
scala> try throw new IllegalArgumentException catch pf1 andThen pf2
pf1
pf2
res0: Int = 0
scala> try throw new NoSuchElementException catch pf1 andThen pf2
java.util.NoSuchElementException
The second PartialFunction is only executed when the first one matched its argument, which can be a problem when you want to catch other exceptions (that also should not return the default value). But for this case, there is orElse:
scala> val pf3: PartialFunction[Throwable, Int] = { case _ => println("pf3"); 1}
pf3: PartialFunction[Throwable,Int] = <function1>
scala> try throw new NoSuchElementException catch pf1 andThen pf2 orElse pf3
pf3
res2: Int = 1
You can use the Try object to wrap your possibly failing code, and then compose the outcome like this
val foo: Int = (Try {
//do stuff that results in an Int
} recover {
//here we handle the recovering
handleFail andThen defaultInt
}).get
val handleFail: PartialFunction[Throwable, Unit] = {
case e: SomeException => //do something if this gets thrown
case e: SomeOtherException => //do something different if this gets thrown
val defaultInt: PartialFunction[Unit, Int] = { case _ => 0 }
I am trying to find a cleaner way to express code that looks similar to this:
def method1: Try[Option[String]] = ???
def method2: Try[Option[String]] = ???
def method3: Try[Option[String]] = ???
method1 match
{
case f: Failure[Option[String]] => f
case Success(None) =>
method2 match
{
case f:Failure[Option[String]] => f
case Success(None) =>
{
method3
}
case s: Success[Option[String]] => s
}
case s: Success[Option[String]] => s
}
As you can see, this tries each method in sequence and if one fails then execution stops and the base match resolves to that failure. If method1 or method2 succeeds but contains None then the next method in the sequence is tried. If execution gets to method3 its results are always returned regardless of Success or Failure. This works fine in code but I find it difficult to follow whats happening.
I would love to use a for comprehension
for
{
attempt1 <- method1
attempt2 <- method2
attempt3 <- method3
}
yield
{
List(attempt1, attempt2, attempt3).find(_.isDefined)
}
because its beautiful and what its doing is quite clear. However, if all methods succeed then they are all executed every time, regardless of whether an earlier method returns a usable answer. Unfortunately I can't have that.
Any suggestions would be appreciated.
scalaz can be of help here. You'll need scalaz-contrib which adds a monad instance for Try, then you can use OptionT which has nice combinators. Here is an example:
import scalaz.OptionT
import scalaz.contrib.std.utilTry._
import scala.util.Try
def method1: OptionT[Try, String] = OptionT(Try(Some("method1")))
def method2: OptionT[Try, String] = OptionT(Try(Some("method2")))
def method3: OptionT[Try, String] = { println("method 3 is never called") ; OptionT(Try(Some("method3"))) }
def method4: OptionT[Try, String] = OptionT(Try(None))
def method5: OptionT[Try, String] = OptionT(Try(throw new Exception("fail")))
println((method1 orElse method2 orElse method3).run) // Success(Some(method1))
println((method4 orElse method2 orElse method3).run) // Success(Some(method2))
println((method5 orElse method2 orElse method3).run) // Failure(java.lang.Exception: fail)
If you don't mind creating a function for each method, you can do the following:
(Try(None: Option[String]) /: Seq(method1 _, method2 _, method3 _)){ (l,r) =>
l match { case Success(None) => r(); case _ => l }
}
This is not at all idiomatic, but I would like to point out that there's a reasonably short imperative version also with a couple tiny methods:
def okay(tos: Try[Option[String]]) = tos.isFailure || tos.success.isDefined
val ans = {
var m = method1
if (okay(m)) m
else if ({m = method2; okay(m)}) m
method3
}
The foo method should do the same stuff as your code, I don't think it is possible to do it using the for comprehension
type tryOpt = Try[Option[String]]
def foo(m1: tryOpt, m2: tryOpt, m3: tryOpt) = m1 flatMap {
case x: Some[String] => Try(x)
case None => m2 flatMap {
case y: Some[String] => Try(y)
case None => m3
}
}
method1.flatMap(_.map(Success _).getOrElse(method2)).flatMap(_.map(Success _).getOrElse(method3))
How this works:
The first flatMap takes a Try[Option[String]], if it is a Failure it returns the Failure, if it is a Success it returns _.map(Success _).getOrElse(method2) on the option. If the option is Some then it returns the a Success of the Some, if it is None it returns the result of method2, which could be Success[None], Success[Some[String]] or Failure.
The second map works similarly with the result it gets, which could be from method1 or method2.
Since getOrElse takes a by-name paramater method2 and method3 are only called if they need to be.
You could also use fold instead of map and getOrElse, although in my opinion that is less clear.
From this blog:
def riskyCodeInvoked(input: String): Int = ???
def anotherRiskyMethod(firstOutput: Int): String = ???
def yetAnotherRiskyMethod(secondOutput: String): Try[String] = ???
val result: Try[String] = Try(riskyCodeInvoked("Exception Expected in certain cases"))
.map(anotherRiskyMethod(_))
.flatMap(yetAnotherRiskyMethod(_))
result match {
case Success(res) => info("Operation Was successful")
case Failure(ex: ArithmeticException) => error("ArithmeticException occurred", ex)
case Failure(ex) => error("Some Exception occurred", ex)
}
BTW, IMO, Option is no need here?