Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pattern matching function signatures with ClassTags/TypeTags

I have a method that explores different parameter values for different classes of functions. Previously, I did some un-typesafe runtime checks, where, post-erasure, all that mattered was that I was using a Function2 versus a Function3. I've taking a stab at using ClassTags/TypeTags to be a bit safer, but I'm still struggling for a good solution. My original reference point was tksfz's answer to:

How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?

import reflect.ClassTag
def matchList[A: ClassTag](list: List[A]) = list match {
  case strlist: List[String] => println("A list of strings!")
  case intlist: List[Int] => println("A list of ints!")
  case _ => println("Ok")
}

However, I found that the snippet as given didn't work, and both cases match a list of strings -- it may be that the answer concerns a different version of scala (I'm using 2.10.3).

I found another example, in which someone gave a snippet using TypeTags (and I'm still a bit hazy on what the difference is between the type tag types; I'd be happy for an RTFM response on that point, provided it's accompanied by a link to a good M to FR).

EDIT: My earlier example didn't apply the functions, so the parts of the matching that produced warnings were pointless (as the first answerer pointed out). I've updated the example so that it's more representative of my actual problem.

package misc
import reflect.runtime.universe._

object CTDemo {

  def main(args: Array[String]) = {
    val f0v: Function2[Int, Double, String] = f0(_, _)
    val f1v: Function2[Int, (Double, Double), String] = f1(_, _)
    println(matchFunc(f0v))
    println(matchFunc(f1v))
  }

  def f0(i: Int, p: Double) = {
    s"output on $p doesn't matter"
  }

  def f1(i: Int, p: (Double, Double)) = {
    s"output on $p doesn't matter"
  }

  def matchFunc[I: TypeTag, A: TypeTag, O: TypeTag](predFunc: Function2[I, A, O]) = {
    predFunc match {
      case fs: Function2[Int, Double, String] if(typeOf[A] <:< typeOf[Double]) => {
        "Single-arg, result is: " + fs(1,2.0)
      }
      case ds: Function2[Int, (Double, Double), String] if(typeOf[A] <:< typeOf[(Double,Double)])  => {
        "Double-arg, result is: " + ds(1,(2.0,3.0))
      }
    }
  }
}

This case works, but it still throws compiler warnings. Is there a succinct, "asInstanceOf"-free, warning-free, and thread-safe way to check the type signature of functions? I'm willing to upgrade to scala 2.11 if that would help.

like image 943
Chris Avatar asked Oct 31 '22 19:10

Chris


1 Answers

Your matches aren't doing any work, so just

  def matchFunc[I: TypeTag, A: TypeTag, O: TypeTag](predFunc: Function2[I, A, O]) = {
      if (typeOf[A] <:< typeOf[Double]) {
        "Single-arg"
      } else if (typeOf[A] <:< typeOf[(Double,Double)]) {
        "Double-arg"
      }
  }

The other idiom works thisaway:

scala> def g[A: reflect.ClassTag](as: List[A]) = as match { case List(_: Int) => "I" }
g: [A](as: List[A])(implicit evidence$1: scala.reflect.ClassTag[A])String

scala> g(List(3))
res4: String = I

scala> g(List(3.0))
scala.MatchError: List(3.0) (of class scala.collection.immutable.$colon$colon)
  at .g(<console>:7)
  ... 33 elided

That is, as a glorified (albeit convenient) isInstanceOf.

In Scala, it's RTFS, where S = spec or SIP.

like image 80
som-snytt Avatar answered Nov 15 '22 07:11

som-snytt