Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I have an impossible case in the scala match?

In the example below, in the second case I would expect the same compile error as in the first case, but it compiles. Why?

object CaseMatching extends App {

  case class Id(value: Long)
  object Id { val zero = Id(0) }
  case class Name(value: String)
  case class IdName(id: Id, name: Name)

  IdName(Id(0), Name("A")) match {
    case IdName(_, Id(0)  ) => // does not compile (as expected)
    case IdName(_, Id.zero) => // does compile (but should not ?)
    case IdName(Id.zero, _) => println("OK") // this is OK and will match
    case _ =>
  }

}

Why is it relevant? - It took me the larger part of an hour to find out why the following case was never met: case TreeEntry(_, Some(child), _, _, NodeType.DIR, _, _) That was, because the NodeType is in the 4th field and not in the 5th field. I would have appreciated if the compiler had told me!

like image 653
Georg Avatar asked Feb 23 '14 09:02

Georg


People also ask

What is Scala match case?

It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C. Here, “match” keyword is used instead of switch statement.

Does Scala have pattern matching?

Pattern matching is the second most widely used feature of Scala, after function values and closures. Scala provides great support for pattern matching, in processing the messages. A pattern match includes a sequence of alternatives, each starting with the keyword case.

What is case class and pattern matching in Scala?

Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.


1 Answers

Shortest answer: making Name final suffices to persuade the compiler that zero is not one. See this issue and environs.

It would warn on a type test, which is an isInstanceOf:

<console>:15: warning: fruitless type test: a value of type CaseMatching.Name cannot also be a CaseMatching.Id
           case IdName(_, _: Id) =>
                             ^

but not when testing equality, since equality is universal.

Here's another good one, case IdName(_, Id) =>

<console>:15: error: pattern type is incompatible with expected type;
 found   : CaseMatching.Id.type
 required: CaseMatching.Name
Note: if you intended to match against the class, try `case _: Id`
           case IdName(_, Id) =>
                          ^

What you want is:

scala> IdName(Id(0), Name("A")) match { case IdName(_, id: Id.zero.type) => }
<console>:21: warning: fruitless type test: a value of type Name cannot also be a Id (the underlying of Id.zero.type)
              IdName(Id(0), Name("A")) match { case IdName(_, id: Id.zero.type) => }
                                                                         ^

The singleton type contains only that value, so it uses eq for the test; and as a type test, it also warns. (It uses eq instead of equals as of this week.)

Not sure how far this goes for you, but:

scala> :pa
// Entering paste mode (ctrl-D to finish)

sealed trait Id { def value: Long }
case class Nonzero(value: Long) extends Id
case object Zero extends Id { val value = 0L }
case class Name(value: String)
case class IdName(id: Id, name: Name)

// Exiting paste mode, now interpreting.

scala> IdName(Zero, Name("A")) match { case IdName(_, Zero) => 1 }
<console>:14: error: pattern type is incompatible with expected type;
 found   : Zero.type
 required: Name
              IdName(Zero, Name("A")) match { case IdName(_, Zero) => 1 }
                                                             ^
like image 174
som-snytt Avatar answered Sep 27 '22 23:09

som-snytt