Scala has a language feature to support disjunctions in pattern matching ('Pattern Alternatives').
x match {
case _: String | _: Int =>
case _ =>
}
However, I often need to trigger an action if the scrutinee satisfies PatternA and PatternB (conjunction.)
I created a pattern combinator '&&' that adds this capability. Three little lines that really remind me why I love Scala!
// Splitter to apply two pattern matches on the same scrutinee.
case object && {
def unapply[A](a: A) = Some((a, a))
}
// Extractor object matching first character.
object StartsWith {
def unapply(s: String) = s.headOption
}
// Extractor object matching last character.
object EndsWith {
def unapply(s: String) = s.reverse.headOption
}
// Extractor object matching length.
object Length {
def unapply(s: String) = Some(s.length)
}
"foo" match {
case StartsWith('f') && EndsWith('f') => "f.*f"
case StartsWith('f') && EndsWith(e) && Length(3) if "aeiou".contains(e) => "f..[aeiou]"
case _ => "_"
}
Points for Discussion
- Is there an existing way to do this?
- Are there problems with this approach?
- Are there other useful combinators that could be created with this approach? (e.g
Not
) - Should such a combinator be added to the standard library?
UPDATE
I've just been asked how the compiler interprets case A && B && C
. These are Infix Operator Patterns (Section 8.1.9 of the Scala Reference). You could also express this with normal Extract Patterns (8.1.7) as &&(&&(A, B), C)
. Notice how the expressions are associated left to right, as per normal infix operator method calls like Boolean#&&
in val b = true && false && true
.