Generic unapply method for different types of List
Asked Answered
B

2

5

Is there a way to generalize this code with generics?

object ListInt {
  def unapply(o: Any): Option[List[Int]] = o match {
    case lst: List[_] if(lst.forall(_.isInstanceOf[Int])) => 
      Some(lst.asInstanceOf[List[Int]])
    case _ => None
  }
}
object ListDouble {
  def unapply(o: Any): Option[List[Double]] = o match {
    case lst: List[_] if(lst.forall(_.isInstanceOf[Double])) =>
      Some(lst.asInstanceOf[List[Double]])
    case _ => None
  }
}
object ListString {
  def unapply(o: Any): Option[List[String]] = o match {
    case lst: List[_] if(lst.forall(_.isInstanceOf[String])) =>
      Some(lst.asInstanceOf[List[String]])
    case _ => None
  }
}

val o: Any = List("a", "b", "c")
o match {
  case ListInt(lst) => println(lst.sum)
  case ListDouble(lst) => println(lst.product)
  case ListString(lst) => println(lst.mkString("(", ", ", ")"))
  case _ => println("no match")
}
Bushweller answered 2/6, 2013 at 17:10 Comment(2)
Maybe this question: #4530062 and the answers can help.Intercellular
How would you like your match clause to look like?Equalizer
E
6
abstract class ListExtractor[A](implicit ct: reflect.ClassTag[A]) {
  def unapply(o: Any): Option[List[A]] = o match {
    case lst: List[_] if (lst.forall(ct.unapply(_).isDefined)) =>
      Some(lst.asInstanceOf[List[A]])
    case _ => None
  }
}

object ListInt    extends ListExtractor[Int   ]
object ListString extends ListExtractor[String]

val o: Any = List("a", "b", "c")
o match {
  case ListInt   (lst) => println(lst.sum)
  case ListString(lst) => println(lst.mkString("(", ", ", ")"))
  case _               => println("no match")
}
Equalizer answered 2/6, 2013 at 19:52 Comment(0)
I
3

It seems TypeTag is the way to go:

import scala.reflect.runtime.universe._

def foo[A: TypeTag](lst: A) = typeOf[A] match {
  case t if t =:= typeOf[List[Int]] => lst.asInstanceOf[List[Int]].sum
  case t if t =:= typeOf[List[Double]] => lst.asInstanceOf[List[Double]].product
  case t if t =:= typeOf[List[String]] => lst.asInstanceOf[List[String]].mkString("(", ", ", ")")
}

println(foo(List("a", "b", "c")))

Check this excellent post for detailed explanation:

Scala: What is a TypeTag and how do I use it?

Implacental answered 2/6, 2013 at 19:54 Comment(3)
However, a List[Any](1, 2, 3) will not produce a List[Int] this way. I don't know if the OP requires that to be the case?Equalizer
Well, I actually need to be able to match on data of type Any but both solutions are really helpful.Bushweller
@Equalizer thanks for pointing it out, I wanted to avoid looping through the list but is seems there is no easy way around.Implacental

© 2022 - 2024 — McMap. All rights reserved.