Why scala pattern matching is not equivalent to isInstanceOf - scala

I believed for a long time that this two constructions is equivalent:
if (myVar.isInstanceOf[MyType]) myVar.asInstanceOf[MyType].doSomething
and
myVar match {
case my : MyType => my.doSomething
case _ => {}
}
But suddenly I've found that I get type error while trying to match Number value to the Double type, but asInstanceOf[Double] works fine. WTF is happening?
simple example for scala REPL
val d = 3.5
val n : Number = d
n.isInstanceOf[Double]
works fine:
Boolean = true
but
n match {
case x : Double => println("double")
case _ => println("not a double")
}
produces type error:
:11: error: pattern type is incompatible with expected type;
found : Double
required: Number
case x : Double => println("double")

scala.Double is not inherited from java.lang.Number but from AnyVal.
You want to match on java.lang.Double:
n match {
case x : java.lang.Double => println("double")
case _ => println("not a double")
}
When using
val d = 3.5
val n : Number = d // implicit conversion from scala.Double to java.lang.Double
scala.Double is implicitly converted to java.lang.Double during assignment to n

Related

Type of pattern binder variable on RHS does not correspond to matched pattern on LHS

Why does the partial function
val warpedEngineers: PartialFunction[Warped[Crewmember], Warped[Engineer]] = {
case v#Warped(Engineer(name: String)) => v.asInstanceOf[Warped[Engineer]]
}
seem to require asInstanceOf cast on RHS whilst the following does not
val engineers: PartialFunction[Crewmember, Engineer] = {
case v#Engineer(name) => v
}
given
sealed trait Crewmember
case class Engineer(name: String) extends Crewmember
case class Commander(name: String) extends Crewmember
case class Warped[+A <: Crewmember](v: A)
val engineers: PartialFunction[Crewmember, Engineer] = {
case v#Engineer(name) => v
}
val warpedEngineers: PartialFunction[Warped[Crewmember], Warped[Engineer]] = {
case v#Warped(Engineer(name: String)) => v.asInstanceOf[Warped[Engineer]]
}
val crew: List[Crewmember] =
List(Engineer("Geordi"), Commander("Picard"), Engineer("Scott"), Commander("Kirk"))
val warpedCrew: List[Warped[Crewmember]] =
List(Warped(Engineer("Geordi")), Warped(Commander("Picard")), Warped(Engineer("Scott")), Warped(Commander("Kirk")))
crew collect engineers
// res0: List[Engineer] = List(Engineer(Geordi), Engineer(Scott))
warpedCrew collect warpedEngineers
// res1: List[Warped[Engineer]] = List(Warped(Engineer(Geordi)), Warped(Engineer(Scott)))
Casting with asInstanceOf could be avoided like so
case Warped(eng: Engineer) => Warped(eng)
but I am wondering why does compiler not insert implicit asInstanceOf and instead types v to Warped[Crewmember]
val warpedEngineers: PartialFunction[Warped[Crewmember], Warped[Engineer]] = {
case v#Warped(Engineer(name: String)) => v
}
Error: type mismatch;
found : Warped[Crewmember]
required: Warped[Engineer]
case v#Warped(Engineer(name: String)) => v
According to SLS 8.1.3: Pattern Binders
A pattern binder 𝑥#𝑝 consists of a pattern variable 𝑥 and a pattern
𝑝. The type of the variable 𝑥 is the static type 𝑇 implied by the
pattern 𝑝. This pattern matches any value 𝑣 matched by the pattern
𝑝, and it binds the variable name to that value.
A pattern 𝑝 implies a type 𝑇 if the pattern matches only values of
the type 𝑇.
Warped(Engineer(name)) on the left in
case v#Warped(Engineer(name: String)) => v
has static type Warped[Crewmember] because that's what you wrote in the type signature
val warpedEngineers: PartialFunction[Warped[Crewmember], ...
So if you write just v on the right it's a type mismatch.
Warped(Engineer(name)) on the right and on the left in
case Warped(Engineer(name)) => Warped(Engineer(name))
look similar but are different because they have different types. They are actually Warped[Crewmember](Engineer(name)) and Warped[Engineer](Engineer(name)). Because of covariance Warped[Engineer] is a Warped[Crewmember] but not vice versa.
How is compiler supposed to guess that it should insert asInstanceOf here and shouldn't for example in val x: Int = "a"?
If you change the signature
val warpedEngineers: PartialFunction[Warped[Engineer], Warped[Engineer]] = {
case v#Warped(Engineer(name)) => v
}
then static type of v will be Warped[Engineer] and the code will compile.
Similarly if you use typed pattern
val warpedEngineers: PartialFunction[Warped[Crewmember], Warped[Engineer]] = {
case v: Warped[Engineer] => v
}
then static type of v will be Warped[Engineer] and the code will compile.
It seems in terms of specification pattern v#Warped(Engineer(name)) in
val warpedEngineers: PartialFunction[Warped[Crewmember], Warped[Engineer]] = {
case v#Warped(Engineer(name)) => ...
}
"implies" type Warped[Crewmember] (because of the signature).

Where does the type erasure happen and what is a safe way to stay away from this?

def decode(l: List[(Int, Symbol)]) : List[Symbol] = {
def foo(r : Int, s: Symbol):List[Symbol] = r match {
case 0 => Nil
case x: Int => s::foo(r-1,s)
}
l match {
case Nil => Nil
case h::tail => foo(h._1 , h._2 ) :+ decode(tail)
}
}
Here I get a compilation error "scala: type mismatch; found:List[Object] required: List[Symbol] case h::tail => foo(h._1 : Int, h._2 : Symbol) :+ decode(tail)
"
It is not a type erasure, simple type mismatch:
foo(h._1 , h._2 ) :+ decode(tail)
Result of foo is List[Symbol], result of decode is List[Symbol]. Now you're trying to put List inside List, no surprize that compiler thinks that the only way to store List and Symbols inside List is to give that later list Object (Any) type.
Most likely you wanted to simply combine two lists:
foo(h._1 , h._2 ) ++ decode(tail)

Auto-unboxing in Scala pattern-match

In the following code, I am getting a compilation error stating that I have a type mismatch on 'x':
val someRef: java.lang.Long = 42L
someRef match {
case x: Long => println("The answer: " + x)
case _ => println("Unknown")
}
How do I get Scala to auto-unbox someRef in the match statement?
The type system doesn't know about boxing at this level. But it does know that if there is an Any, a boxed Long is really (presumably) supposed to be just a Long (from the AnyVal part of the class inheritance tree). So:
val someRef: java.lang.Long = 42L
(someRef: Any) match {
case x : Long => println("The answer is " + x)
case _ => println("What answer?")
}

How can I match classes in a Scala "match" statement?

How can I use a "match" statement to identify the value of a class variable? The following is invalid, and I can't find an acceptable variant -- other than if ... else if ... else ...
val c: Class[_] = classOf[Int]
val what = c match { case classOf[Int] => "int!"; case classOf[Float] => "float!" }
The compiler complains: error: not found: type classOf
And of course, I can't use Class[Int] because that type information is erased:
c match { case Class[Int] => "int!"; case Class[Float] => "float!" }
error: type Class of type Class does not take type parameters.
I've also tried variants like Int.class, all to no avail. (And I don't really want to convert to strings: I feel it's important to have the compiler catch renamed/moved classes.)
Am I being dense, or have I stumbled into a Scala blind spot?
The verbose case comparison works:
val what = c match {
case q if q == classOf[Int] => "int!"
case q if q == classOf[Float] => "float!"
}
Of course, being a lower-case identifier, classOf should not work directly in a case statement anyway. However, neither does an escaped
case `classOf`[Int]
work in this case, so you’ll have to go with the if-guard.
You can match on class values if you create a stable identifier (ie. a val) for them,
scala> val c: Class[_] = classOf[Int]
c: Class[_] = int
scala> val ClassOfInt = classOf[Int]
ClassOfInt: java.lang.Class[Int] = int
scala> val ClassOfFloat = classOf[Float]
ClassOfFloat: java.lang.Class[Float] = float
scala> val what = c match {
| case ClassOfInt => "int!"
| case ClassOfFloat => "float!"
| }
what: String = int!
Note that you can't match on type (ie. Class[Int]) because erasure means that the different type instantiations of Class[T] are indistinguishable at runtime ... hence the warning below
scala> val what = c match {
| case _: Class[Int] => "int!"
| case _: Class[Float] => "float!"
| }
warning: there were 2 unchecked warnings; re-run with -unchecked for details
what: java.lang.String = int!
I encountered the same problem and placing the class in a 'stable identifier' wasn't that practical. I found the next best thing was to have tidy 'else if' statements.
Using this method:
private def is[T <: AnyRef : Manifest](implicit cls: Class[_]) =
cls == manifest[T].runtimeClass
I can write:
implicit val arg = cls
if (is[ClassA]) ...
else if (is[ClassB]) ...
...
else throw new IllegalArgumentException("Unknown class: " + cls)
To consider inheritance:
val what = c match {
case q if classOf[Int].isAssignableFrom(q) => "int!"
case q if classOf[Float].isAssignableFrom(q) => "float!"
}

scala either pattern match

what is wrong in this piece of code?
(Left("aoeu")) match{case Right(x) => ; case Left(x) => }
<console>:6: error: constructor cannot be instantiated to expected type;
found : Right[A,B]
required: Left[java.lang.String,Nothing]
why the pattern matcher just doesn't skip the Right and examine Left?
Implicit typing is inferring that Left("aoeu") is a Left[String,Nothing]. You need to explicitly type it.
(Left("aoeu"): Either[String,String]) match{case Right(x) => ; case Left(x) => }
It seems that pattern matching candidates must always be of a type matching the value being matched.
scala> case class X(a: String)
defined class X
scala> case class Y(a: String)
defined class Y
scala> X("hi") match {
| case Y("hi") => ;
| case X("hi") => ;
| }
<console>:11: error: constructor cannot be instantiated to expected type;
found : Y
required: X
case Y("hi") => ;
^
Why does it behave like this? I suspect there is no good reason to attempt to match on an incompatible type. Attempting to do so is a sign that the developer is not writing what they really intend to. The compiler error helps to prevent bugs.
scala> val left: Either[String, String] = Left("foo")
left: Either[String,String] = Left(foo)
scala> left match {
| case Right(x) => "right " + x
| case Left(x) => "left " + x }
res3: java.lang.String = left foo