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!
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.
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.
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.
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 }
^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With