Let's say that I have n values in Scala named v1, v2, ..., vi, ..., vn of types Tij that are not necessarily different types for different i. I want to pattern match against the n values using a custom logic.
One way to do it is to nest all possibilities in case I need to be exhaustive (which for the sake of this example I need to be, otherwise I could use placeholder mag_
c) and I can't merge branches (which for the sake of this example I can't as every custom logic is unique):
v1 match {
case x1: T11 => v2 match {
case x2: T21 => v3 match {
...
case xn_1: Tn_11 => vn match {
case xn: Tn1 => // Custom logic 1.
case xn: Tn2 => // Custom logic 2.
...
case xn: Tnk => // I am already laughing, but I have to write it down:
// Custom logic k.
}
...
...
}
case x2: T22 => v3 match {
// I guess you get the point.
}
...
case x1: T12 => v2 match {
// And so on until exhaustion in every meaning of the word.
}
... // These three dots are needed here. Now I feel whole.
}
The other option is to flatten the whole darn thing out:
(v1, v2, ..., vn) match {
case (x1: T11, x2: T21, ... xn: Tn1) => // Custom logic 1.
case (x1: T11, x2: T21, ... xn: Tn2) => // Custom logic 1.
...
case (x1: T11, x2: T21, ... xn: Tnk) => // Custom logic k (with a hearthy chuckle).
... // Three dots saving my soul and my finger joints.
}
While the nested version avoids duplicate typing, it can lead to hard-to-read code due to indentation overflow when n is high (and we are not).
On the other hand, the flattened version contains a lot of duplicate code, but is easier to interpret.
Also, the nested version seems to be more performant as the checking of xi happens maximum once per type Tij (but perhaps I should not care about such things as the JVM could just optimize it all away, and I don't want to be all evil).
Which one is idiomatic Scala code and is therefore recommended? Is there a performance difference between the two versions?
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.
It turned out that pattern matching in their example was significantly faster than if-else tests. Even though the code doesn't utilize any special pattern match cases that would not be possible with if-else tests, it just compares integers.
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.
You should pick the option that most closely expresses the meaning of your code and not worry about performance. If the performance of this match
is critical to your code then you have bigger problems with your design. (It is also not clear that one performs better than the other, so choosing based on assumed performance would be unwise).
If every case
leads to an independent piece of code, then having a flat match
is the most direct expression of the logic. Adding spurious nesting is just going to confuse things.
If there is some common code between two or more case
expressions then they can be grouped into nested match
statements so that the common code is not duplicated. This may also apply if there is some logical commonality between multiple cases that you want to expresses in the code.
Also note that you can chain partial functions using orElse
which allows you to split one big match
into separate functions with meaningful names, while avoiding nested match
statements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With