0

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")
        }
    }
}
3
  • The use of Any, isInstanxeOf checks, asInstanceOf casts, and the use of a print to signal an error are all code smells... I understand that you are probably learning and this is just for practice, but IMHO you should practice following the best practices of the language. What exactly did you want to make here? There are probably better solutions. Commented Dec 13, 2019 at 12:26
  • Hi Luis, Your assumption is correct, I'm just learning the language and trying to create a UDF for Spark job. My first attempt was try and achieve this with generic type (commented method). Solutions offered have helped clear the concepts for me. Commented Dec 15, 2019 at 9:30
  • Uhm, I do not know much about Spark UDFs, but I believe you are not allowed to use sophisticated things like typeclasses (which would be the best solution for this problem). So I would either just go with overloading or leave the casts. Even if Spark is written in Scala and you use Scala to talk with Spark, they are generally two different worlds, so it may be worth asking this same question in the context of UDFs, maybe there is another way to handle this kind of scenarios in that world. Commented Dec 15, 2019 at 13:22

3 Answers 3

3

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)
Sign up to request clarification or add additional context in comments.

3 Comments

A typeclass would be a better implementation for this.
@LuisMiguelMejíaSuárez I've added typeclass example but I'm not sure I'm able to make a detailed explanation in English.
2

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 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

3 Comments

Thanks. This seems appropriate.
I just tried to cast it as Int to get around this. x.asInstanceOf[Int] +1. Is this a good practice? Do you also know why the commented increment method won't work?
Casts are never a good practice. They are also pretty much never required. The reason why your other method doesn't work is that you have declared x to be of an unknown type. Since the type is unknown, you can't use the + method, since you don't know whether the type that is passed in even has such a method.
1

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
        }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.