Removing "eliminated by erasure" warning in Scala - scala

I have a simple Scala function that generates a Json file from a Map[String, Any].
def mapToString(map:Map[String, Any]) : String = {
def interpret(value:Any) = {
value match {
case value if (value.isInstanceOf[String]) => "\"" + value.asInstanceOf[String] + "\""
case value if (value.isInstanceOf[Double]) => value.asInstanceOf[Double]
case value if (value.isInstanceOf[Int]) => value.asInstanceOf[Int]
case value if (value.isInstanceOf[Seq[Int]]) => value.asInstanceOf[Seq[Int]].toString.replace("List(", "[").replace(")","]")
case _ => throw new RuntimeException(s"Not supported type ${value}")
}
}
val string:StringBuilder = new StringBuilder("{\n")
map.toList.zipWithIndex foreach {
case ((key, value), index) => {
string.append(s""" "${key}": ${interpret(value)}""")
if (index != map.size - 1) string.append(",\n") else string.append("\n")
}
}
string.append("}\n")
string.toString
}
This code works fine, but it emits a warning message in the compilation.
Warning:(202, 53) non-variable type argument Int in type Seq[Int] (the underlying of Seq[Int])
is unchecked since it is eliminated by erasure
case value if (value.isInstanceOf[Seq[Int]]) =>
value.asInstanceOf[Seq[Int]].toString.replace("List(", "[").replace(")","]")
^
The line case value if (value.isInstanceOf[Seq[Int]]) causes the warning, and I tried case value #unchecked if (value.isInstanceOf[Seq[Int]]) to removed the warning, but it does not work.
How to remove the warning?

If you don't really care about the component type (and it seems you do not, as all you do is stringify it):
case value if (value.isInstanceOf[Seq[_]]) =>
value.asInstanceOf[Seq[_]].toString.replace("List(", "[").replace(")","]")
Come to think of it, you should be able to call toString on anything anyway:
case value if (value.isInstanceOf[Seq[_]]) =>
value.toString.replace("List(", "[").replace(")","]")
but instead of toString followed by messing with the String consider Seq#mkString
value.mkString("[", ",", "]")
Finally, that pattern isInstanceOf/asInstanceOf can be replaced by a match (in all the cases)
case value: Int => value // it's an Int already now, no cast needed
case value: Seq[_] => value.mkString("[", ",", "]")

You could do the following,
case value: String => ???
case value: Double => ???
case value: Int => ???
case value: Seq[Int] #unchecked => ???
or as #Thilo mentioned
case value: Seq[_] =>

This is the better code that generates no warning message (with the hints from Thilo & Ɓukasz).
def mapToString(map:Map[String, Any]) : String = {
def interpret(value:Any) = {
value match {
case value:String => "\"" + value + "\""
case value:Double => value
case value:Int => value
case value:Seq[_] => value.mkString("[",",","]")
case _ => throw new RuntimeException(s"Not supported type ${value}")
}
}
map.toList.map { case (k, v) => s""" "$k": ${interpret(v)}""" }.mkString("{\n", ",\n", "\n}\n")
}

Related

Scala Generic method errors on parameter operation

I get "type mismatch; found : Int(1) required: String" error when I try to return the incremented value of the input parameter from a Scala generic method below.
I did try using the Case method for this but it did not work as well. Basically I want to decide the operation based on input Type to the method and return the calculated/modified value.
object GenericOperations {
// def increment[typ](val x:typ):typ = x match {
// case _:Int => x + 1
// case _:String => x + "x"
// }
def increment2(x:Any):Any = {
if(x.isInstanceOf[Int]) {
x+1
}
else if (x.isInstanceOf[String]) {
x + "x"
}
else {
println("No Match type")
}
}
}
I would rather use method overloading:
def increment2(x: Int) = x + 1
def increment2(x: String) = x + "x"
If you are sure you need exactly one function you may use match.
def increment2(x: Any): Any = x match {
case v: Int => v + 1
case v: String => v + "x"
case _ =>
throw new Exception("No Match type")
}
But returning Any isn't good thing as you cannot use its result without type cast
GenericOperations.increment2(3) + 3 // type mismatch
Still you may use the same match way:
def increment2[T](x: T): T = (x match {
case v: Int => v + 1
case v: String => v + "x"
case _ => throw new Exception("No Match type")
}) match {
case v: T => v
case _ => throw new Exception("Invalid increment expression result type")
}
As it has been mentioned in the comments there is also typeclass way:
//type class
trait Incrementable[T] {
def inc(x: T): T
}
//type class instance for String
implicit val incString = new Incrementable[String] {
def inc(x: String) = x + "x"
}
//type class instance for Int, single abstract method (SAM) form
implicit val incInt: Incrementable[Int] = (x: Int) => x + 1
def increment2[T: Incrementable](x: T): T = implicitly[Incrementable[T]].inc(x)
You have declared x to be of type Any. Which means you are only allowed to use the methods of Any.
You are calling x.+(1). There is no + method in Any. Therefore, you can't use +.
You should be getting an error about + not existing, but you don't, so what's happening here?
There is an implicit conversion for string concatenation, which can convert an arbitrary object into a String and then concatenate another String to it. In this case, it converts x to an any2stringadd and then tries to add 1 to it, but the any2stringadd.+ method only takes a String as its argument, and thus you get the strange error message that it is expecting a String.
You, however, are passing 1 as an argument, which is an Int not a String.
Note that any2stringadd is deprecated in Scala 2.13, so in the future you would just get an error about a non-existent method.
Note that you have tagged this question with generics and also talk about generics multiple times in the subject and the question body, yet there are no generics in your code. In fact, with generics, this problem would not exist.
See also
Scala Beginner trying to use Parameterised Type
Type parameter in scala
Maybe something like this , even though I still don't like it because the usage of getOrElse , but here you go anyway:
object GenericOperations {
// def increment[typ](val x:typ):typ = x match {
// case _:Int => x + 1
// case _:String => x + "x"
// }
def increment2(x:Any):Any = {
if(x.isInstanceOf[Int]) {
toInt(x).getOrElse(0)+1
}
else if (x.isInstanceOf[String]) {
x + "x"
}
else {
println("No Match type")
}
}
def toInt(x: Any): Option[Int] = x match {
case i: Int => Some(i)
case _ => None
}
}

How to define an empty Tuple?

I have the following function definition:
private def extractUrl: String => (String, String)
= url =>
url
.split("/")
.toList
.filter(_.startsWith("localhost"))
.flatMap(e => e.split(":").toList)
.foldLeft[(String, String)](("", "")) { (acc, x) =>
acc match {
case ("", "") => (x, "")
case (a, "") => (a, x)
case z => z
}
}
the question is, is there another way to define an empty Tuple instead of ("", "")?
Empty tuple
("", "") is a tuple of empty strings with type (String, String).
Empty is unclear in that context, could be (None, None) or even (null, null) (bad)
You seem to use "" to represents a value that is not present. Try using None and Some[String], both sub types of Option[String], to indicate that a value is not present.
Analysis and comments
Potentially, your method seems not to do what is intended. (execute code below)
Think about using already present functions / methods / libraries for manipulating URLs (also see example below)
Think about using Option
object Fun {
import java.net.URL
def main(args: Array[String]): Unit = {
val url1 = "http://localhost:4000/a/b/c?x=1&y=2#asdf"
val url2 = "http://example.com:4000/a/localhostb/c?x=1&y=2#asdf"
val urls = List(url1, url2)
// your approach
println("Your approach")
urls.map( urlString => extractUrl(urlString ))
.foreach(println)
println("New approach")
urls.map(x => extractUrl2(x))
.filter( x => x.host.startsWith("localhost") )
.foreach(println)
}
case class HostPort(host: String, port: Option[String])
def extractUrl2: String => HostPort = urlString => {
val url = new URL(urlString)
HostPort(url.getHost,
url.getPort match {
case -1 => None
case i => Some(i.toString)
})
}
def extractUrl: String => (String, String) = url =>
url
.split("/")
.toList
.filter(_.startsWith("localhost"))
.flatMap(e => e.split(":").toList)
.foldLeft[(String, String)](("", "")) { (acc, x) =>
acc match {
case ("", "") => (x, "")
case (a, "") => (a, x)
case z => z
}
}
}
yields
Your approach
(localhost,4000)
(localhostb,)
New approach
HostPort(localhost,Some(4000))
I don't think it is possible to define an empty Tuple.
I tried to use (->) but that resolves to a Tuple2.type and not a Tuple2.
If the values of your Tuple are optional, use the type system to express that:
(Option[String], Option[String])
Better yet, you could define a case class for your data structure:
case class HostAndPort(host: Option[String], port: Option[String])
This would provide default values for each type within the tuple:
let my_tuple: (String, usize) = Default::default();

Scala: pattern matching over a reflect.runtime.universe.Type?

how can I do a pattern match over a reflect.runtime.universe.Type?
def test(t: reflect.runtime.universe.Type) {
t match {
case Int => \\ ...
case Double => \\ ...
case String => \\ ...
case _ => \\ ...
}
}
This dosn't work, as the interpreter complains:
error: pattern type is incompatible with expected type;
found : Int.type
required: reflect.runtime.universe.Type
Note: if you intended to match against the class, try `case _: Int`
case Int => // ...
^
Trying the suggestion does not work either:
def test(t: reflect.runtime.universe.Type) {
t match {
case _: Int => \\ ...
case _: Double => \\ ...
case _: String => \\ ...
case _ => \\ ...
}
}
...
error: pattern type is incompatible with expected type;
found : Int
required: reflect.runtime.universe.TypeApi
case _: Int => // ...
^
So what is the correct syntax for this?
Thanks!
The TypeTag API has a comparison operator =:= and a method for obtaining the Type instance for a given class, you can combine these with guards to obtain the desired result:
import scala.reflect.runtime.universe._
def test(t: Type) {
t match {
case t if t =:= typeOf[Int] => println("int")
case t if t =:= typeOf[String] => println("string")
case _ =>
}
}
As lmm and n1r3 pointed out, you might be better off to use an if here. However, you could do something like the following:
import reflect.runtime.universe._
case class TypeComparer[A](typeToCompare: TypeTag[A]){
def unapply(universeType: Type) = universeType =:= typeToCompare.tpe
}
object TypeComparer{
val IntComparer = new TypeComparer(typeTag[Int])
}
object TestObject{
def tester(t: Type) {
t match {
case TypeComparer.IntComparer() => println("pass")
case _ => println("fail")
}
}
def test[A](implicit ev: TypeTag[A]) = tester(typeOf[A])
}
which you can run like:
TestObject.test[Int] //pass
TestObject.test[Boolean] //fail
The downside is that you need to create a concrete object for every comparer as I do not know of a way to have companion object with a type used in the apply
So, it is a bit verbose....and the if is probably better here.
Ok, I found a workaround:
def test(t: reflect.runtime.universe.Type) {
t.toString match {
case "Int" => \\ ...
case "scala.Int" => \\ ...
case "Double" => \\ ...
case "scala.Double" => \\ ...
case "String" => \\ ...
case _ => \\ ...
}
}
However, is there a better solution that avoids going via strings?

How to pattern-match Class[X] for different X?

I want to check the type of the parameters of a method, but I don't know the best way to do this. See my code:
class X {
def x(a: Int, b: String) {}
}
val methods = classOf[X].getDeclaredMethods
methods map { m =>
m.getParameterTypes.toList map { t =>
println(t.getName)
// I don't know how to write the following
if ( the type of t is Int) { do something}
else if( the type of t is String ) { do something}
else { }
}
}
Please note the comment in the code. I don't know how to check the types in scala way.
I've tried:
t match {
case _:String => println("### is a string")
case _:Int => println("### is an int")
case _ => println("### ?")
}
But it can't be compiled.
I can use java-way to check:
if (t.isAssignableFrom(classOf[String])) // do something
else if(t.isAssignableFrom(classOf[Int])) // do something
else {}
It seems we should use it in scala, right?
UPDATE:
If I want to use match, I should write like this:
t match {
case i if i.isAssignableFrom(classOf[Int]) => println("### is an Int")
case s if s.isAssignableFrom(classOf[String]) => println("### is a String")
case _ => println("###?")
}
Is it the best answer?
I could make it work with t as a type by defining the cases as constants. It wouldn't compile with the class literals as the case expression. Try:
val S = classOf[String]
val I = classOf[Int]
t match {
case S => println("### is a string")
case I => println("### is an int")
case _ => println("### ?")
}
You can use ClassManifest.fromClass to correctly handle the coercion of primitives to AnyVals, and any other such troubles you might have encountering boxed vs unboxed types when getting funky with reflection.
Like this:
import reflect.ClassManifest
class Wibble { def method(a:Int, b: String) = () }
for(method <- classOf[Wibble].getDeclaredMethods; paramType <- method.getParameterTypes) {
ClassManifest.fromClass(paramType) match {
case m if m <:< ClassManifest.Int => println("Interiffic")
case m if m <:< ClassManifest.Float => println("Floaty, like butterflies")
case m if m <:< ClassManifest.Double => println("Or Quits...")
//todo: all the other AnyVal types...
case m if m <:< classManifest[String] => println("bleeding edge physics, yeah baby!")
//...and a default condition
}
}

Optional function parameter with generic return type

How would you implement class that parses some input via regex and transforms founded string to some other type? My approach is:
class ARegex[T](regex:Regex, reform:Option[String => T]){
def findFirst(input:String):Option[T] = {
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s) // this won't compile because of type mismatch
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
class BRegex[T](regex:Regex, reform:Option[String => T]) {
def findFirst(input:String) = { //returns Option[Any] - erasure
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s)
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
We can solve this problem by eliminating the Option part of the reform's type, and using a different mechanism to indicate that we don't want to change the match in any way. This mechanism is to use identity as a default parameter or pass identity when you don't want the type to change.
class ARegex[T](regex:Regex, reform:String => T = identity[String](_)){
def findFirst(input:String):Option[T] = {
regex.findFirstIn(input) match{
case None => None
case Some(s) => Some(reform(s))
}
}
}
new ARegex("something".r).findFirst("something else") //returns Option[String]
new ARegex("3".r, {x=>x.toInt}).findFirst("number 3") //returns Option[Int]
Well, the problem is the type mismatch, because you are returning either a String or a T, which, of course, are unified at Any. You can't say you are going to return Option[T] and then return Option[String].
Other than that, a simplified version of that code is this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Any] =
regex findFirstIn input map { s => reform map (_(s)) getOrElse s }
}
You could return an Option[Either[String, T]], though. The code would look like this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Either[String, T]] =
regex findFirstIn input map { s => reform map (_(s)) toRight s }
}
Why is reform Option[String => T] instead of just String => T? If you don't pass in a mechanism for creating an instance of your desired type, there's no mechanism for the runtime system to actually create the appropriate object. If you really need to pass in an Option[String => T] then your second case should simply return None.
Also, flatMap is your friend, and will give you the correct behavior (i.e. if reform is None, the method returns None.
class RegexExtractor[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[T] = reform.flatMap(f => regex.findFirstIn(input).map(f))
}