4

I noticed that there is no || operator available when pattern matching - is | short circuited?

JasonG
  • 5,794
  • 4
  • 39
  • 67
  • Official SLS says that - The alternative pattern matches a value v if at least one its alternatives matches v, so i think that would be at least logical, to be short-circuted, cause this operator is not boolean or, but a denotes a pattern alternative – 4lex1v Oct 21 '13 at 20:34
  • See also: [Pattern matching with conjunctions (PatternA AND PatternB)](http://stackoverflow.com/questions/2261358/pattern-matching-with-conjunctions-patterna-and-patternb) – J Cracknell Oct 21 '13 at 20:38

2 Answers2

10

In pattern matching, | is short circuited. You can't call unapply or the like (with returned parameters) with the or-operator, where side-effects might be more likely. So short-circuiting is purely an optimization technique (won't affect the correctness of the code except in extraordinary cases such as a side-effecting equals method). This does mean you are limited in your ability to short circuit or not for performance or side-effecting reasons.

To see this, if we write this code:

def matchor(s: String) = s match {
  case "tickle" | "fickle" => "sickle"
  case _ => "hammer"
}

We see this bytecode (in part)

public java.lang.String matchor(java.lang.String);
  Code:
   0:   aload_1
   1:   astore_2
   2:   ldc #12; //String tickle
   4:   aload_2
   5:   astore_3
   6:   dup
   7:   ifnonnull   18
   10:  pop
   11:  aload_3
   12:  ifnull  25
   15:  goto    31
   18:  aload_3
   19:  invokevirtual   #16; //Method java/lang/Object.equals:(Ljava/lang/Object;)Z
   22:  ifeq    31
   25:  iconst_1
   26:  istore  4
   28:  goto    66
   31:  ldc #18; //String fickle
   33:  aload_2
   ...
   66:  iload   4
   68:  ifeq    78
   71:  ldc #20; //String sickle
   73:  astore  6
   75:  goto    82
   ...
   82:  aload   6
   84:  areturn

See the jump on line 28 to avoid testing the "fickle" case? That's the short-circuit.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
3

| short-circuits.

object First {
    def unapply(str: String): Boolean = {
        println("in First")
        str == "first"
    }
}

object Second {
    def unapply(str: String) = {
        println("in Second")
        str == "second"
    }
}

object Run extends App {
    "first" match {
        case First() | Second() => None
    }

    //Output: In First

    "first" match {
        case Second() | First() => None
    }

    //Output: In Second\nIn First
}