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()
  }
}
1
  • 2
    I think you meant A: ClassTag/A: TypeTag, Manifests are kind of deprecated. Check this guide. Commented Jun 3, 2015 at 18:14

3 Answers 3

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

1 Comment

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

1 Comment

This level of implicits is very dangerous imho.
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.

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.