pattern matching in scala works inside a def, otherwise does not

In the first code snippet below, the case statement is defined inside a function and it works as expected.

Code snippet 1

def echoWhatYouGaveMe(x: Any): String = x match {

    case (a, b) => s"got $a and $b"
    case (a, b, c) => s"got $a, $b, and $c"
    case _ => "Unknown"
}

object MatchTest extends App {

    // trigger the tuple patterns
    println(echoWhatYouGaveMe((1,2)))         // two element tuple
    println(echoWhatYouGaveMe((1,2,3)))       // three element tuple

}

MatchTest.main(Array("dummy"))

Output

got 1 and 2

got 1, 2, and 3

The case below is not within a function, but is otherwise very similar to above. It gives an error. I understand the error, but what I do not understand is why I am getting an error below and not an error above.

Code Snippet 2

val myTuple = (1, 2, 3)

val toPrint = myTuple match {
  case (a, b, c) => s"got $a, $b, and $c"
  case (a, b) => s"got $a and $b"
  case _ => s"Unknown data"
}

Output

:14: error: constructor cannot be instantiated to expected type; found : (T1, T2) required: (Int, Int, Int) case (a, b) => s"got $a and $b"

Upvotes: 1

Views: 276

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170733

When you write

val myTuple = (1, 2, 3)

the type of myTuple is inferred to be (Int, Int, Int). So when you match it, all patterns must be compatible with this type, i.e. there need to be values of this type which would match the pattern. Otherwise the pattern is useless and can as well be removed. case (a, b) is such a useless pattern.

In snippet 1 the pattern isn't useless, because it is compatible with Any.

Upvotes: 0

Chaitanya
Chaitanya

Reputation: 3638

The reason for such behavior of scala pattern matching lies in the type system. Scala has a very strong type system which allows high type safety and catches errors at compile time itself. If you declare a function having parameter instead of Any, it will also throw the same error.

For ex :-

def echoWhatYouGaveMe(x: (Int, Int)): String = x match {

  case (a, b) => s"got $a and $b"
  case (a, b, c) => s"got $a, $b, and $c"
  case _ => "Unknown"
}

echoWhatYouGaveMe((1,2))

You get an error stating

Error:(4, 9) constructor cannot be instantiated to expected type;
 found   : (T1, T2, T3)
 required: (Int, Int)
  case (a, b, c) => s"got $a, $b, and $c"
       ^

If you modify your val myTuple and explicitly set its type to Any, it will allow you to perform pattern matching.

val myTuple: Any = (1, 2, 3)

val toPrint = myTuple match {
  case (a, b, c) => s"got $a, $b, and $c"
  case (a, b) => s"got $a and $b"
  case _ => s"Unknown data"
}

You get an output like this

myTuple: Any = (1,2,3)

toPrint: String = got 1, 2, and 3

Upvotes: 9

Related Questions