2

I would like to do something like this:

implicit class MyString(s: String) {
  def getAs[T]: T = {
    T match {
      case q if q == classOf[Int] => s.toInt
      case q if q == classOf[Boolean] => s.toBoolean
    }
  }
}

This doesn't compile, of course. How do I write this so it does?

aa8y
  • 3,854
  • 4
  • 37
  • 62
  • I believe that this helpful StackOverflow [answer](http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and-how-do-i-use-it) will address your question. – Kevin Meredith Nov 11 '15 at 14:38

4 Answers4

4

Consider this approach:

object Parse {
  def parse[T](f:String => Option[T]) = f
  implicit val parseInt = parse(s => Try(s.toInt).toOption)
  implicit val parseLong = parse(s => Try(s.toLong).toOption)
  implicit val parseDouble = parse(s => Try(s.toDouble).toOption)
  implicit val parseBoolean = parse(s => Try(s.toBoolean).toOption)
}

implicit class MyString(s:String) {
  def getAs[T]()(implicit run: String => Option[T]): Option[T] = run(s)
}

Usage:

def main(args: Array[String]) {
  import Parse._
  "true".getAs[Boolean].foreach(println)
  "12345".getAs[Int].foreach(println)
}
Nyavro
  • 8,806
  • 2
  • 26
  • 33
2

When use classOf for type match, there maybe will have some issues, example:

scala> classOf[List[Int]] == classOf[List[String]]
res17: Boolean = true
scala> typeOf[List[Int]] =:= typeOf[List[String]]
res18: Boolean = false

classOf only store the class information not with generics type

typeOf will store full type information

TypeTags and Manifests

class MyString(s: String) {
  def getAs[T](implicit tag: TypeTag[T]): T = {
    tag.tpe match {
      case t if t =:= typeOf[Int] => s.toInt.asInstanceOf[T]
    }
  }
}
scala> new MyString("123")
res2: MyString = MyString@7fca02

scala> res6.getAs[Int]
res3: Int = 123

use TypeType tag to get type info, and call getAs with type information.

chengpohi
  • 14,064
  • 1
  • 24
  • 42
  • What is `tag.tpe`? Couldn't find it in the docs with a Ctrl+F. – aa8y Nov 11 '15 at 06:04
  • `tpe` is the method of `TypeTag`, is used to get type `Type`(tag). and the `tpe` type is [TypeRef](http://www.scala-lang.org/api/2.10.4/index.html#scala.reflect.api.Types$TypeRef) – chengpohi Nov 11 '15 at 06:09
  • Well, I tried using the method `getAs[T]` in another method which has a Type parameter and ended up with this error: `No TypeTag available for T`. Looks like that can't be done with this. – aa8y Nov 11 '15 at 06:18
1

This is what I have come up with so far:

import reflect.runtime.universe.TypeTag
import scala.reflection.runtime.universe._

implicit class MyString(s: String) {
  def getAs[T : TypeTag]: T = {
    typeOf[T] match {
      case t if t =:= typeOf[Int] => s.toInt.asInstanceOf[T]
      case t if t =:= typeOf[Boolean] => s.toBoolean.asInstanceOf[T]
    }
  }
}

Running with this in REPL:

scala> "32".getAs[Int]
res25: Int = 32

scala> "32".getAs[Boolean]
java.lang.IllegalArgumentException: For input string: "32"
  at scala.collection.immutable.StringLike$class.parseBoolean(StringLike.scala:290)
  at scala.collection.immutable.StringLike$class.toBoolean(StringLike.scala:260)
  at scala.collection.immutable.StringOps.toBoolean(StringOps.scala:30)
  at MyString.getAs(<console>:33)
  ... 43 elided

scala> "false".getAs[Int]
java.lang.NumberFormatException: For input string: "false"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.lang.Integer.parseInt(Integer.java:580)
  at java.lang.Integer.parseInt(Integer.java:615)
  at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
  at scala.collection.immutable.StringOps.toInt(StringOps.scala:30)
  at MyString.getAs(<console>:32)
  ... 43 elided

scala> "false".getAs[Boolean]
res28: Boolean = false

scala> "false".getAs[String]
scala.MatchError: String (of class scala.reflect.internal.Types$AliasNoArgsTypeRef)
  at MyString.getAs(<console>:31)
  ... 43 elided

Better, I think, would be to return an option[T], and catch any issues such as a type T that isn't catered for, or attempting a cast that will fail. I started mucking around with scala's Try (and its .toOption method), but hit some odd errors so haven't gotten any further with that.

EDIT: just using a simple try-catch, we can get:

implicit class MyString(s: String) {
  def getAs[T : TypeTag]: Option[T] = try {
    typeOf[T] match {
      case t if t =:= typeOf[Int] => Some(s.toInt.asInstanceOf[T])
      case t if t =:= typeOf[Boolean] => Some(s.toBoolean.asInstanceOf[T])
    }
  } catch {
    case ex: Exception => None
  }
}

Resulting in:

scala> "false".getAs[String]
res30: Option[String] = None

scala> "32".getAs[Boolean]
res31: Option[Boolean] = None

scala> "32".getAs[Int]
res32: Option[Int] = Some(32)

scala> "true".getAs[Boolean]
res33: Option[Boolean] = Some(true)

scala> "true".getAs[Int]
res34: Option[Int] = None
Shadowlands
  • 14,994
  • 4
  • 45
  • 43
0

Based on the answers given by @chengpohi and @Shadowlands, this is what I came up with.

object ImplicitsStartingWithS {
  implicit class MyString(s: String) {
    val str = s.trim
    import reflect.runtime.universe.TypeTag
    import scala.reflect.runtime.universe._

    def getAs[T](implicit tag: TypeTag[T]): Option[T] = {
      val value = tag.tpe match {
        case t if t =:= typeOf[Int] => str.toIntStr.map(_.toInt)
        case t if t =:= typeOf[Long] => str.toIntStr.map(_.toLong)
        case t if t =:= typeOf[Float] => str.toNumericStr.map(_.toFloat)
        case t if t =:= typeOf[Double] => str.toNumericStr.map(_.toDouble)
        case _ => None
      }
      value.asInstanceOf[Option[T]]
    }

    def toDecimalStr = "^-*\\d+\\.\\d+$".r.findFirstIn(s)
    def toIntStr = "^-*\\d+$".r.findFirstIn(s)
    def toNumericStr = {
      s.toDecimalStr match {
        case Some(decimalStr) => Some(decimalStr)
        case None => s.toIntStr
      }
    }
  }
}

This avoids exception handling for faster response.

aa8y
  • 3,854
  • 4
  • 37
  • 62
  • I would suggest going with Nyavro's approach instead if at all possible. Nyavro's answer is normal Scala code; this TypeTag stuff is unusual and should be considered a last resort. Also, have you actually benchmarked that regex processing is faster than an exception? I'm skeptical. – Seth Tisue Nov 11 '15 at 13:17