I am using a Java library which returns an object of type Object. Now I want to pattern match and get the appropriate type. I expect it to be a Java Map. So, I tried using this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.collection.JavaConverters._
val any: Any = new java.util.HashMap[Object, Object]
Option(any).flatMap {
case x: java.util.Map[_, _] => Some(x.asScala.toMap)
case x: Map[_, _] => Some(x)
case _ => None
}
// Exiting paste mode, now interpreting.
<console>:17: error: no type parameters for method flatMap: (f: Any => Option[B])Option[B] exist so that it can be applied to arguments (Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ }
required: Any => Option[?B]
Option(any).flatMap {
^
<console>:17: error: type mismatch;
found : Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ }
required: Any => Option[B]
Option(any).flatMap {
^
Not sure what I am doing wrong here.
The below works. The compiler doesn't have the enough information to derive the type here since we are using existential types for our Maps in the pattern matching. This is because unlike Java Map is not a type in scala but Map[T,U] is
Option(any).flatMap[Any]({
case x: java.util.Map[_, _] => Some(x.asScala.toMap)
case x: Map[_, _] => Some(x)
case _ => None
})
If we dont use existential type as shown below, we would be able to use the flatMap without the explicit type parameter specified
scala> Option(any).flatMap({
| case x: java.util.Map[Int, Int] #unchecked => Some(x.asScala.toMap) // using Int as example to create a fully qualified type of Map
| case x: Map[Int, Int] #unchecked => Some(x) // using Int as example to create a fully qualified type of Map
| case _ => None
| })
res5: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map())
Related
I asked this question earlier: Combine a PartialFunction with a regular function
and then realized, that I haven't actually asked it right.
So, here goes another attempt.
If I do this:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse { case x => x.toString }
it does not compile: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: PartialFunction[?,?]
But this works fine:
val x: Seq[String] = List(1,2,3).collect { case x => x.toString }
The question is what is the difference? The type of the argument is the same in both cases: PartialFunction[Int, String]. The value passed in is literally identical. Why one does one case work, but not the other?
You need to specify the type for bar because the compiler is unable to infer it. This compiles:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar : (Int => String) = foo orElse { case x => x.toString }
In the case of List(1,2,3).collect{case x => x.toString} the compiler is able to infer the input type of the partial function based off of how theList was typed.
final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That])
Based on the type parameters the compiler can infer that you are passing a correctly typed partial function. That's why List(1,2,3).collect{case x:String => x.toString} does not compile nor does List(1,2,3).collect{case x:Int => x.toString; case x: String => x.toString}.
Since List is covariant the compiler is able to infer that the partial function {case x => x.toString} is a partial function on Int. You'll notice that List(1,2,3).collect{case x => x.length} does not compile because the compiler is inferring that you're operating on either an Int or a subclass of Int.
Also keep in mind that the {case x => x.toString} is just syntactic sugar. If we do something like the below then your example works as expected
val f = new PartialFunction[Int, String](){
override def isDefinedAt(x: Int): Boolean = true
override def apply(v1: Int): String = v1.toString
}
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse f //This compiles fine.
List(1,2,3).collect{f} // This works as well.
So the only logical answer from my perspective is that the syntactic sugar that is able to generate a PartialFunction instance for {case x => x.toString} does not have enough information at compile time to be able to adequately type it as a PartialFunction[Int, String] in your orElse case.
You can use the library Extractor.scala.
import com.thoughtworks.Extractor._
// Define a PartialFunction
val pf: PartialFunction[Int, String] = {
case 1 => "matched by PartialFunction"
}
// Define an optional function
val f: Int => Option[String] = { i =>
if (i == 2) {
Some("matched by optional function")
} else {
None
}
}
// Convert an optional function to a PartialFunction
val pf2: PartialFunction[Int, String] = f.unlift
util.Random.nextInt(4) match {
case pf.extract(m) => // Convert a PartialFunction to a pattern
println(m)
case f.extract(m) => // Convert an optional function to a pattern
println(m)
case pf2.extract(m) => // Convert a PartialFunction to a pattern
throw new AssertionError("This case should never occur because it has the same condition as `f.extract`.")
case _ =>
println("Not matched")
}
Consider the following Scala code snippet:
def func(param: Any): Int = param match {
case f: (String => Int) => f("apple")
case i: Int => i
}
println(func((s: String) => s.length))
Works as expected, however, at compilation I get the following warning:
<console>:11: warning: non-variable type argument String in type pattern String => Int is unchecked since it is eliminated by erasure
case f: (String => Int) => f("apple")
How can I get rid of this warning message?
Thanks your help in advance!
The reason why you get the message is because of Java's generic type erasure. In this particular case, your function which is of type Function[String, Int] will be matched by any Function[A, B].
In order to get rid of this warning you should use scala typetags which will allow you to differentiate between the different function types.
The code snippet is below,
import scala.reflect.runtime.universe._
object Answer {
def function[A](param: A)(implicit tt: TypeTag[A]): String = param match {
case f: (String => Int) #unchecked if typeOf[String => Int] =:= typeOf[A] => f("apple").toString
case f: (Int => String) #unchecked if typeOf[Int => String] =:= typeOf[A] => f(32 + 1)
case s: String => s"hello $s"
}
def main (args: Array[String]) {
println(function((s: String) => s.length))
println(function((i: Int) => i.toString))
println(function("world"))
}
}
The key part is to have an implicit TypeTag[A] which is added at compile time which includes the metadata that the function typeOf needs to check the types of A against anything else.
(Really awful title.)
Anyway: can I somehow make Scala infer the type of b in 2nd line?
scala> class A[B](val b: B, val fun: B => Unit)
defined class A
scala> new A("123", b => { })
<console>:9: error: missing parameter type
new A("123", b => { })
^
This works as expected after adding the type:
scala> new A("123", (b: String) => { })
res0: A[String] = A#478f6f48
And String is certainly the expected type:
scala> new A("123", (b: Int) => {})
<console>:9: error: type mismatch;
found : Int => Unit
required: String => Unit
new A("123", (b: Int) => {})
^
For such cases in Scala, like in many other languages, the concept of currying exists:
scala> class A[B](val b: B)(val fun: B => Unit)
defined class A
scala> new A("string")(_.toUpperCase)
res8: A[String] = A#5884888a
You also can simplify this with case classes:
scala> case class A[B](b: B)(fun: B => Unit)
defined class A
scala> A("string")(_.toUpperCase)
res9: A[String] = A(string)
As for your example:
new A("123", (b: Int) => {})
You can't do this, both arguments in class declaration have generic B type, so both parameters must have the same type
I am trying to compile this scala code and getting the following compiler warnings.
scala> val props: Map[String, _] =
| x match {
| case t: Tuple2[String, _] => {
| val prop =
| t._2 match {
| case f: Function[_, _] => "hello"
| case s:Some[Function[_, _]] => "world"
| case _ => t._2
| }
| Map(t._1 -> prop)
| }
| case _ => null
| }
<console>:10: warning: non-variable type argument String in type pattern (String, _) is unchecked since it is eliminated by erasure
case t: Tuple2[String, _] => {
^
<console>:14: warning: non-variable type argument _ => _ in type pattern Some[_ => _] is unchecked since it is eliminated by erasure
case s:Some[Function[_, _]] => "world"
^
The answers given on How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections? seems to point to this same issue. But I couldn't infer a solution in this particular context.
Use case t # (_: String, _) instead of case t: Tuple2[String, _] and case s # Some(_: Function[_, _]) instead of case s:Some[Function[_, _]].
Scala cannot match on type parameters.
I would rewrite it like this:
x match {
case (name, method) => {
val prop =
method match {
case f: Function[_, _] => "hello"
case Some(f: Function[_, _]) => "world"
case other => other
}
Map(name -> prop)
}
case _ => null
}
Tried this:
scala> 2.isInstanceOf[AnyVal]
<console>:8: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
2.isInstanceOf[AnyVal]
^
and this:
scala> 12312 match {
| case _: AnyVal => true
| case _ => false
| }
<console>:9: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
case _: AnyVal => true
^
The message is very informative. I get that I can't use it, but what should I do?
I assume you want to test if something is a primitive value:
def testAnyVal[T](x: T)(implicit evidence: T <:< AnyVal = null) = evidence != null
println(testAnyVal(1)) // true
println(testAnyVal("Hallo")) // false
println(testAnyVal(true)) // true
println(testAnyVal(Boolean.box(true))) // false
I assume that your type is actually Any or you'd already know whether it was AnyVal or not. Unfortunately, when your type is Any, you have to test all the primitive types separately (I have chosen the variable names here to match the internal JVM designations for the primitive types):
(2: Any) match {
case u: Unit => println("Unit")
case z: Boolean => println("Z")
case b: Byte => println("B")
case c: Char => println("C")
case s: Short => println("S")
case i: Int => println("I")
case j: Long => println("J")
case f: Float => println("F")
case d: Double => println("D")
case l: AnyRef => println("L")
}
This works, prints I, and does not give an incomplete match error.