Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala pattern matching on (Try,Try)

Tags:

scala

I have the the following Tuple - (t1,t2) :(Try,Try)

I want to check if both succeeded or if one of them failed, but avoid code duplication. Something like:

(t1,t2) match {
case (Success(v1),Success(v2)) => new MyClass(v1,v2)
case (Failure(e),_) | (_,Failure(e)) => println(e.getMessage)
}

of course the 2nd statement won't work since I need to give different extraction variables. but then I have to check them since I don't know which failed and actually contains Throwable. I wish Try would act like Future so it'll have Try.sequence(t1,t2).

Any idea how to make this work elegantly?

like image 768
Hagai Avatar asked Mar 05 '17 15:03

Hagai


People also ask

Does Scala have pattern matching?

Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

How does Scala pattern matching work?

Pattern matching is a way of checking the given sequence of tokens for the presence of the specific pattern. It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C.

What is case _ in Scala?

case _ => does not check for the type, so it would match anything (similar to default in Java). case _ : ByteType matches only an instance of ByteType . It is the same like case x : ByteType , just without binding the casted matched object to a name x .

Which method of case class allows using objects in pattern matching?

The match method takes a number of cases as an argument. Each alternative takes a pattern and one or more expressions that will be performed if the pattern matches.


2 Answers

You can convert it to a Try[MyClass] like this:

val myclass = for {
  v1 <- t1
  v2 <- t2
} yield new MyClass(v1, v2)

If t1 failed, or both t1 and t2 failed, myclass will be a Failure with the Exception for t1. If only t2 failed, myclass will be a Failure with the Exception for t2. Otherwise, myclass will be a Success. You can then process it normally using recover or whatever.

like image 155
Karl Bielefeldt Avatar answered Dec 08 '22 00:12

Karl Bielefeldt


You could do a tail recursive call for the (_, Failure(e)) case:

@annotation.tailrec
def apply(t1: Try[Any], t2: Try[Any]): Any =
  (t1, t2) match {
    case (Success(v1), Success(v2)) => new MyClass(v1,v2)
    case (Failure(e), _) => println(e.getMessage)
    case _ => apply(t2, t1)
  }

Cats lets you do this elegantly. For any F[_]: Traverse and G[_]: Applicative, it defines the equivalant of Future.sequence:

def sequence(fa: F[G[A]]): G[F[A]]

The library also provides out of the box instances for Try. Further reading in the traverse documentation.

like image 34
OlivierBlanvillain Avatar answered Dec 08 '22 00:12

OlivierBlanvillain