1

How can I write a function to convert a String to a generic type (Double, Int, Float, etc.)?

Here's pseudocode for the functionality I'm looking for:

def castFromString[A: Manifest](value: String): A = {
  if (A == Double) {
    parseDouble(value)
  } else if (A == Int) {
    parseInt(value)
  } else {
    value.toString()
  }
}
Corey Wu
  • 1,209
  • 1
  • 22
  • 39
  • 2
    I think you meant `A: ClassTag`/`A: TypeTag`, `Manifest`s are kind of deprecated. Check [this guide](http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html). – Gábor Bakos Jun 03 '15 at 18:14

3 Answers3

3

I don't think there is a way to write the function as such with Manifest, ClassTag or a TypeTag as they can only tell you the type at runtime. For something like the pseudocode function to work, the return type must be known at compile time.

However, could implement the function by defining a type class and writing instances for the types you wish to cast from. Here's an example:

import java.lang.Double.parseDouble
import java.lang.Integer.parseInt

trait CastableFromString[A] {
  def fromString(string: String): A
}

object CastableFromString {
  implicit object DoubleCastableFromString extends CastableFromString[Double] {
    override def fromString(string: String): Double =
      parseDouble(string)
  }

  implicit object IntCastableFromString extends CastableFromString[Int] {
    override def fromString(string: String): Int =
      parseInt(string)
  }

  implicit object IntListCastableFromString extends CastableFromString[List[Int]] {
    override def fromString(string: String): List[Int] =
      string.split(',').map(parseInt).toList
  }

  def castFromString[A](string: String)(implicit cast: CastableFromString[A]): A =
    cast.fromString(string)
}

object Main {
  import CastableFromString._

  def main(args: Array[String]): Unit = {
    val d = castFromString[Double]("4.5")
    val i = castFromString[Int]("42")
    val li = castFromString[List[Int]]("1,2,3")
    println(s"d=$d i=$i li=$li")
  }
}

Alternatively, to get closer to something that resembles your pseudocode, you could use a macro. Here's an example (won't work on anything older than Scala 2.11):

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

class StringCasterImpl(val c: Context) {
  def castFromStringImpl[A: c.WeakTypeTag](string: c.Expr[String]): c.Tree = {
    import c.universe._
    val t = c.weakTypeOf[A]
    if (t =:= typeOf[Double]) {
      q"java.lang.Double.parseDouble($string)"
    } else if (t =:= typeOf[Int]) {
      q"java.lang.Integer.parseInt($string)"
    } else if (t =:= typeOf[List[Int]]) {
      q"$string.split(',').map(java.lang.Integer.parseInt).toList"
    } else {
      c.abort(c.enclosingPosition, s"Don't know how to cast $t to String")
    }
  }
}

object StringCaster {
  def castFromString[A](string: String): A =
    macro StringCasterImpl.castFromStringImpl[A]
}

// note that this object must be in a separate compilation unit
object Main {
  import StringCaster._

  def main(args: Array[String]): Unit = {
    val d = castFromString[Double]("4.2")
    val i = castFromString[Int]("42")
    val li = castFromString[List[Int]]("1,2,3")
    println(s"d=$d i=$i li=$li")
  }
}
Community
  • 1
  • 1
Olli Helenius
  • 439
  • 4
  • 5
  • I like scala, but this is just one of those several "craptastic broken windows" where java does it much better with a one word solution (instead of requiring a hierarchy of objects/classes). – Andrew Norman May 24 '16 at 18:26
0

You can define implicit functions that converts string to specific type.

Then when required it will automatically casted.

math.max(1,"3") // throws error

but now

implicit def StringToInt(x: String): Int = x

math.max(1,"3")  //>> 3
Mok
  • 277
  • 1
  • 6
  • 16
0

If you want to do it exactly this way, you can match the runtimeClass in the ClassTag:

def castFromString[A: ClassTag](value: String): A = {
  val c = implicitly[ClassTag[A]].runtimeClass
  if(c == classOf[Double]) parseDouble(value).asInstanceOf[A]
  else ...
}

Note the need for an asInstanceOf cast. The typeclass approach suggested by @Olli is safer and more idiomatic.

lmm
  • 17,386
  • 3
  • 26
  • 37