Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write "asInstanceOfOption" in Scala

Tags:

scala

Is it possible to write an "asInstanceOfOption" method that would do what is intended by the following (bogus) code?

def asInstanceOfOption[T](o: Any): Option[T] =
   if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None 
like image 253
Matt R Avatar asked Nov 26 '09 11:11

Matt R


2 Answers

EDIT Below is my original answer but you can accomplish this now with

def asInstanceOfOption[T: ClassTag](o: Any): Option[T] = 
  Some(o) collect { case m: T => m}

You could use manifests to get around the fact that the type T is erased at compile time:

scala> import scala.reflect._
import scala.reflect._

scala> def asInstanceOfOption[B](x : Any)(implicit m: Manifest[B]) : Option[B] = {
   | if (Manifest.singleType(x) <:< m)
   |   Some(x.asInstanceOf[B])
   | else
   |   None
   | }
asInstanceOfOption: [B](x: Any)(implicit m: scala.reflect.Manifest[B])Option[B]

Then this could be used:

scala> asInstanceOfOption[Int]("Hello")
res1: Option[Int] = None

scala> asInstanceOfOption[String]("World")
res2: Option[String] = Some(World)

You could even use implicit conversions to get this to be a method available on Any. I think I prefer the method name matchInstance:

implicit def any2optionable(x : Any) = new { //structural type
  def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
    if (Manifest.singleType(x) <:< m)
      Some(x.asInstanceOf[B])
    else
      None
  }   
}

Now you can write code like:

"Hello".matchInstance[String] == Some("Hello") //true
"World".matchInstance[Int] == None             //true    

EDIT: updated code for 2.9.x, where one can't use Any but only AnyRef:

implicit def any2optionable(x : AnyRef) = new { //structural type
  def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
    if (Manifest.singleType(x) <:< m)
      Some(x.asInstanceOf[B])
    else
      None
  }   
}
like image 133
oxbow_lakes Avatar answered Sep 24 '22 16:09

oxbow_lakes


Here's an elaboration on oxbow_lake's updated answer, further updated to require Scala 2.10:

// Implicit value class
implicit class Castable(val obj: AnyRef) extends AnyVal {
  def asInstanceOfOpt[T <: AnyRef : ClassTag] = {
    obj match {
      case t: T => Some(t)
      case _ => None
    }
  }
}

This can be used by doing:

"Hello".asInstanceOfOpt[String] == Some("Hello") // true
"foo".asInstanceOfOpt[List[_]] == None // true

However as mentioned in other answers this doesn't work for primitives because of boxing issues, nor does it handle generics because of erasure. To disallow primitives, I restricted obj and T to extend AnyRef. For a solution that handles primitives, refer to the answer for Matt R's followup question:

How to write asInstanceOfOpt[T] where T <: Any

Or use shapeless's Typeable, which handles primitives as well as many erasure cases:

Type casting using type parameter

like image 28
tksfz Avatar answered Sep 26 '22 16:09

tksfz