2

This returns Any:

def convertType(data: String, dataType: String) = {
  dataType match {
    case "Int" => data.toInt
    case "Double" => data.toDouble
    case "Long" => data.toLong
    case "Option[Int]" => Some(data.toInt)
    case "Option[Long]" => Some(data.toLong)
    case "Option[Double]" => Some(data.toDouble)
   }
}

// Isn't this a bad way to solve the problem ?
implicit def anyToInt(str: Any) = str.asInstanceOf[Int]
implicit def anyToLong(str: Any) = str.asInstanceOf[Long]

val i: Int = convertType("1", "Int")
val l: Long = convertType("1", "Long")

What is the best way to keep the original data type intact?

Sahil Sareen
  • 1,813
  • 3
  • 25
  • 40

2 Answers2

0
  import scala.reflect.runtime.universe._

  implicit class StringConverter(data: String) {
    private[this] def convert(tag: Type): Any = tag match {
      case t if t =:= typeOf[Int] => data.toInt
      case t if t =:= typeOf[Double] => data.toDouble
      case t if t =:= typeOf[Long] => data.toLong
      case t if t =:= typeOf[Option[Int]] => Some(data.toInt)
    }
    def convert[T](implicit typeTag: TypeTag[T]): T = convert(typeTag.tpe).asInstanceOf[T]
  }
  scala> "123".convert[Int]
  res5: Int = 123

  scala> "123".convert[Option[Int]]
  res6: Option[Int] = Some(123)

  scala> "123".convert[Double]
  res7: Double = 123.0

As @Luka Jacobowitz said, convert type with generics:

  • implicit TypeTag with generic to retrieve type info
  • Type with case match to parse data
  • asInstanceOf to cast variable to target type(because it already has checked by TypeTag, it's safe to do that)
chengpohi
  • 14,064
  • 1
  • 24
  • 42
0

What you want to do is slightly tricky. Subtyping, as you've seen, isn't going to get you what you want due to having to return the supertype of all your return types.

Type variables though, might save the day!

def convertType[T](x: String): T = x.asInstanceOf[T]

But that's not quite what you want... You need something that has different behavior based on what T is being passed. Something like (not real Scala)

def convertType[T](x: String): T = T match {
  case Double => x.toDouble
  case Int => x.toInt
  // And all your other cases
}

Unfortunately Scala is not a full-fledged dependently-typed language so we don't have functions that can act directly on types, so that match statement doesn't work.

However, if we provided a value-level "witness" for the type T, maybe we could get away with our conversion.

def convertType[T](x: String)(witness: T): T = witness match {
  case _: Double => x.toDouble
  case _: Int => x.toInt
  // Fill in other cases as necessary
}

We can make this slightly more convenient for ourselves by making the witness implicit. Now that only gets us half-way there because we still need to have weird dummy implicit values sitting around that don't do anything except be witnesses to our function (e.g. randomly we'll need something like implicit val z = 2).

Can we get rid of the need to create these dummy values to pass in as witnesses?

Yes and there's two ways. One is to use Scala's TypeTags which are exactly meant to be value level witnesses of types generated automatically by the compiler.

import scala.reflect.runtime.universe._

def convertTypes[T](x: String)(implicit witness: TypeTag[T]): T = witness match {
  case a if a =:= typeOf[Int] => x.toInt
  case a if a =:= typeOf[Double] => x.toDouble
}

Alternatively you can move the actual conversion out to a separate trait and have witnesses implement the behavior specified by the trait.

trait Convertable[T] {
  def convertFromString(x: String): T
}

implicit object ConvertInt extends Convertable[Int] {
  override def convertFromString(x: String): Int = x.toInt
}

// Do the same for your other types

def convertType[T](x: String)(implicit witness: Convertable[T]): T = witness.convertFromString(x)

And there we go! A nice type-safe-ish conversion method (at least this last one is). You'd probably want to wrap the entire thing in a scala.util.Try in case you pass a malformed string.

Incidentally we've just recreated typeclasses (or used Scala's built-in TypeTag typeclass).

Community
  • 1
  • 1
badcook
  • 3,699
  • 14
  • 25