Paul Draper
Paul Draper

Reputation: 83393

Scala generic type matching

Frequently, I find myself doing pattern matching such as this

val foo: Foo = ...
foo match {
  case bar: Bar => Some(bar)
  case _ => None
}

I want to make this shorter.

val foo: Foo = ...
optInstance[Foo, Bar](foo)

Possibility 1 - direct translation

def optInstance[A, B <: A](a: A) = a match {
  case b: B => Some(b)
  case _ => None
}
// warning: abstract type pattern B is unchecked since it is eliminated by erasure

Possibility 2 - try-catch

def optInstance[A, B <: A](a: A) =
  try {
    Some(a.asInstanceOf[B])
  } catch {
    case e: ClassCastException => None
  }
// no warnings

Possiblility 3 - if-else

def optInstance[A, B <: A](a: A) =
  if(a.isInstanceOf[B]) {
    Some(a.asInstanceOf[B])
  } else {
    None
  }
// no warnings

None of them work. (Scala 2.11.2)


Is there a generic way of writing

foo match {
  case bar: Bar => Some(bar)
  case _ => None
}

(If not, is there at least a shorter way?)

Upvotes: 1

Views: 161

Answers (3)

Lomig M&#233;gard
Lomig M&#233;gard

Reputation: 1838

Cleaner way using implicit value class:

 implicit class AsOpt[A](val a: A) extends AnyVal {
   def asOpt[B <: A : scala.reflect.ClassTag]: Option[B] = a match {
     case b: B => Some(b)
     case _    => None
   }
 }

Example:

 val seq: Seq[Int] = Seq.empty
 seq.asOpt[List[Int]]   // Option[List[Int]] = Some(List())
 seq.asOpt[Vector[Int]] // Option[Vector[Int]] = None

Upvotes: 2

Dimitri
Dimitri

Reputation: 1786

Just add an implicit ClassTag for your first implementation:

import scala.reflect.ClassTag

def optInstance[A, B <: A : ClassTag](a: A): Option[B] = a match {
  case b: B => Some(b)
  case _ => None
}

Example:

sealed trait Foo
case object Bar extends Foo
case object Baz extends Foo

scala> optInstance[Foo, Bar.type](Bar)
res4: Option[Bar.type] = Some(Bar)

scala> optInstance[Foo, Bar.type](Baz)
res5: Option[Bar.type] = None

Upvotes: 4

Michael Zajac
Michael Zajac

Reputation: 55569

Without a generic function you can use Option and collect:

class Foo
class Bar extends Foo
class Baz extends Foo

scala> val foo: Foo = new Bar
scala> Some(foo).collect { case b: Bar => b }
res1: Option[Bar] = Some(Bar@483edb6b)

scala> val baz: Foo = new Baz
scala> Some(baz).collect { case b: Bar => b }
res3: Option[Bar] = None

Upvotes: 1

Related Questions