170

Is it possible to match on a comparison using the pattern matching system in Scala? For example:

a match {
    case 10 => println("ten")
    case _ > 10 => println("greater than ten")
    case _ => println("less than ten")
}

The second case statement is illegal, but I would like to be able to specify "when a is greater than".

2
  • 2
    This can also be used to check if a function evaluates to true, e.g. case x if x.size > 2 => ... Commented Sep 7, 2010 at 14:53
  • 2
    The important thing to understand is that the "patterns" to the left of => operator are indeed "patterns". The 10 in the first case expression you have is NOT the integer literal. So, you can't perform operations (like > check or say function application isOdd(_)) on the left. Commented May 1, 2013 at 21:22

5 Answers 5

318

You can add a guard, i.e. an if and a boolean expression after the pattern:

a match {
    case 10 => println("ten")
    case x if x > 10 => println("greater than ten")
    case _ => println("less than ten")
}

Edit: Note that this is more than superficially different to putting an if after the =>, because a pattern won't match if the guard is not true.

Sign up to request clarification or add additional context in comments.

1 Comment

Ben, good answer, it really illustrates the importance of pattern guard.
34

As a non-answer to the question's spirit, which asked how to incorporate predicates into a match clause, in this case the predicate can be factored out before the match:

def assess(n: Int) {
  println(
    n compare 10 match {
      case 0 => "ten"
      case 1 => "greater than ten"
      case -1 => "less than ten"
    })
}

Now, the documentation for scala.math.Ordering.compare(T, T) promises only that the non-equal outcomes will be greater than or less than zero. Java's Comparable#compareTo(T) is specified similarly to Scala's. It happens to be conventional to use 1 and -1 for the positive and negative values, respectively, as Scala's current implementation does, but one can't make such an assumption without some risk of the implementation changing out from underneath.

6 Comments

I'm not sure if you're suggesting this as a real solution, but I would strongly recommend against anything that relies on an undocumented convention or assumption.
Exactly. That's why I wrote "one can't make such an assumption without some risk", and qualified my answer as a "non-answer". It's interesting to consider why compare() and compareTo() don't specify 0, 1, and -1 as their codomain.
Math.signum(n compare 10) would guarantee -1, 0 or 1.
This morning I confirmed that nearly six years after writing my original answer, even though the implementation in question moved from one type to another, Scala still maintains that noted behavior of returning -1, 0, or 1.
A valid answer, but personally I don't like this. It's too easy to forget what 0,1, and -1 are supposed to mean.
|
27

A solution that in my opinion is much more readable than adding guards:

(n compare 10).signum match {
    case -1 => "less than ten"
    case  0 => "ten"
    case  1 => "greater than ten"
}

Notes:

  • Ordered.compare returns a negative integer if this is less than that, positive if greater, and 0 if equal.
  • Int.signum compresses the output from compare to -1 for a negative number (less than 10), 1 for positive (greater than 10), or 0 for zero (equal to 10).

Comments

1

While all the above and bellow answers perfectly answer the original question, some additional information can be found in the documentation https://docs.scala-lang.org/tour/pattern-matching.html , they didn't fit in my case but because this stackoverflow answer is the first suggestion in Google I would like to post my answer which is a corner case of the question above.
My question is:

  • How to use a guard in match expression with an argument of a function?

Which can be paraphrased:

  • How to use an if statement in match expression with an argument of a function?

The answer is the code example below:

    def drop[A](l: List[A], n: Int): List[A] = l match {
      case Nil => sys.error("drop on empty list")
      case xs if n <= 0 => xs
      case _ :: xs => drop(xs, n-1)
    }

link to scala fiddle : https://scalafiddle.io/sf/G37THif/2 as you can see the case xs if n <= 0 => xs statement is able to use n(argument of a function) with the guard(if) statement.

I hope this helps someone like me.

Comments

0

Scala's pattern matching allows you to define your own extractor. In this case, you can simply define a new extractor:

class GreaterThan(n: Int) {
  def unapply(i: Int) = i > n
}

val GreaterThan10 = GreaterThan(10)

a match {
  case 10 => ???
  case GreaterThan10() => ???
  case _ => ???
}

or just use pattern guards.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.