Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - pattern-matching a tuple of related types

Tags:

I have the following class hierarchy:

class A class B extends A class C extends A 

then, there is another class which takes instances of these classes and there is a method, in which two cases of pattern-matching are possible like this:

class D (one: A, two: A) {    def work {     (one, two) match {       case (o, t): (B, B) => ... blablabla       case (o, t): (B, C) => ... blablabla       case _ =>     }   } } 

However, when it should resolve the matching in favor of the second case (B, C), it tries resolving it as (B, B) and comes up with the class cast exception that C cannot be cast to B. Why? What to do? How can I come around this?

like image 568
noncom Avatar asked Aug 16 '12 12:08

noncom


People also ask

Does Scala have pattern matching?

Notes. 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 class and pattern matching in Scala?

It is defined in Scala's root class Any and therefore is available for all objects. 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. A symbol => is used to separate the pattern from the expressions.


Video Answer


2 Answers

Your syntax isn't quite right (doesn't compile).

This works though:

object Matcher extends App {    class A   class B extends A   class C extends A    class D(one: A, two: A) {      def work {       (one, two) match {         case (o: B, t: B) => println("B")         case (o: B, t: C) => println("C")         case _ =>       }     }   }    val d1 = new D(new B, new B)   val d2 = new D(new B, new C)    d1.work   //B   d2.work   //C } 
like image 52
Brian Smith Avatar answered Sep 28 '22 04:09

Brian Smith


The problem, as always, is erased types. (B,C) is syntactic sugar for Tuple2[B,C], which is erased to Tuple2 at runtime. The case statement verifies that (B,C) matches Tuple2, but then fails to cast it.

In your case, the easiest solution would be to match against 'one' and 'two' individually, rather than wrapping them in a tuple:

one match {   case o : B => two match {     case p : C => ...     case p : B => ...   }   ...  } 

It's not so pretty, but it won't suffer from the same problems.

Edit: Actually, I'd go with Brian Smith's solution - matching inside the tuple rather than outside. It avoids the problem in a similar way, but looks nicer.

like image 38
Submonoid Avatar answered Sep 28 '22 04:09

Submonoid