Search code examples
scalatuplespattern-matchingscala-option

Scala - Match Tuple of Options


I have:

val foo = Some(List(1, 2, 3)) -> Some("y")

I would like to cast match it:

foo match {
    case (Some(x), Some(y)) => println(x + " " + y)
    case _ => println("error")

This works fine for Some(List(1, 2, 3) -> Some("score")) but fails for Some(List(1, 2, 3) -> None, None -> Some("y") or None -> None with:

error: constructor cannot be instantiated to expected type;
     found   : Some[A]
     required: None.type
error: not found: value ...

Why is that?

Of course I could use getOrElse() but that does not look so elegant.

Thx a lot, Karsten

Update:

foo match {
 case (x: Some[List[Int]], y: Some[Int]) => println(x.get)
 case _ => println("error")
}

Fails as well with:

error: pattern type is incompatible with expected type;
 found   : Some[Int]
 required: None.type

I would think that case _ would take care of that.


Solution

  • It is the compiler telling you something. If you have an expression like

    val foo = Some(List(1, 2, 3)) -> None
    

    It will have the type (Some[List[Int]], None.type), which you can easily see from typing in the expression in the scala console

    scala> val foo = Some(List(1, 2, 3)) -> None
    foo: (Some[List[Int]], None.type) = (Some(List(1, 2, 3)),None)
    

    So you know at compile time that the second element of the tuple can only ever be None, a match with Some can never succeed. Hence the error message.

    If you give foo a less restrictive type it will work.

    val foo : (Option[List[Int]], Option[String]) = Some(List(1, 2, 3) -> None
    

    Note that this is exactly as it should be. Matching on something that can never happen is almost definitely an error. You have to upcast to any to avoid the compile time error (but then you will get a runtime error.