0
votes

Is it possible to have Scala keep pattern matching down the class hierarchy after successfully matched a parent class? For example, I'm expecting the following code to print 1 and 2, but after matching S against T, Scala won't keep going and matching the next case, so the next case will never be reachable.

class T
class S extends T

new S match {
  case _: T => println(1)
  case _: S => println(2)
}
4
So you want it to print BOTH 1 and 2? - Ruslan

4 Answers

4
votes

The first matching case is always selected. Just swap the order and test for the subtype S before testing for the parent class T. Or you can have a nested pattern match.

1
votes

The closest i can think of would look like this ,

class T
class S extends T

new S match {
 case c@ (_: T  | _: S ) => println(1)
}

This way you could check if new S is of either of the types.

1
votes

Think of pattern matching as a sequence of if-else if - else -if ... statements. You cannot execute more than one branch. The way around it is, depending on the complexity of your selectors, either nesting:

new S match {
   case t: T => println(1)
       t match {
         case _: S => println(2)
       }
}

or duplicating some of the actions in multiple branches:

new S match {
   case _: S => println(2); println(1)
   case _: T => println(1)
}

Or else using partial functions, which may be an overkill for your case, but just for completeness:

Option(new S).map { 
  case s:S => println(2); s
  case s => s
}.collect { 
  case _:T => println(1) 
}
1
votes

Scala pattern matching does not support fall-throughs. I actually asked this exact question a while ago (Scala pattern matching - match multiple successful cases)

You would need to do it independently for every possible case.

If your actual use case is different (i.e. you have dozens of these conditions and you need to execute them sequentially in an elegant way) - I would possibly opt to use a different method and not pattern matching at all.

Possibly, what I would do (to maintain a functional paradigm) would be something like this:

class T
class S extends T

val myTestedVal = new S

((x:T) => ( 
    (x match { case z:S => (()=>println(2)) case _ => } ) ::
    (x match { case z:T => (()=>println(1)) case _ => } ) ::
    // Add other cases here
     Nil)) (myTestedVal).collect { case i:(()=>Unit) => i }.foreach(z => z())

The above sample code would print both 2 and 1 for S, and just 1 for T. What it does, is it basically runs the list of conditions, creating a list of functions to run based on the successful conditions, and then executes them in the foreach (which could also be modified to be a map if you need the functions to return meaningful values).

Note

The above method is definitely overkill for the OP's simple use-case. However, if you have multiple conditions that you want to execute in a "rule-engine" style, the above method is more elegant and "functional" than having a long chain of "if" statements.

Disclaimer: I am somewhat new to Scala and FP myself.