3

In following code I want the hitA to be called only when the type of i is A. The type to check is provided as a type parameters, therefore it is type erased and a match fails on it, giving me a compiler warning:

abstract type pattern T is unchecked since it is eliminated by erasure

The desired output of the code is:

Hit A

Not hit

In current version when I run the code I get Hit A followed by a ClassCastException.

I understand what is happening and why the warning and the exception is there, but I am not sure how to get around this. I have read basic articles on TypeTags and I understand how to use them in basic cases, but I fail to see how could I create a partial function using TypeTags.

import scala.reflect.runtime.universe._


object TestMatch extends App {
  abstract class I

  class A extends I {
    def hitA() = println("Hit A")
  }

  class B extends I

  def processOpGen[T <: I : TypeTag](op: PartialFunction[I, Unit])(i: I) = {
    val fallback : PartialFunction[I, Unit] = {case _ => println("Not hit")}
    (op orElse fallback)(i)
  }

  def partialMatchGen[T <: I : TypeTag](op: T => Unit)(i: I) = {
    processOpGen[T] {
      case c: T => op(c) // can TypeTag be used here for matching somehow?
    }(i)
  }


  partialMatchGen[A](a => a.hitA())(new A)
  partialMatchGen[A](a => a.hitA())(new B)

}
Suma
  • 33,181
  • 16
  • 123
  • 191
  • This post might help - http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and-how-do-i-use-it. I was using it to try to answer this question. What are you looking to match on this line: `case c: T => op(c)`? – Kevin Meredith Oct 13 '15 at 14:16
  • I want to perform some action on `c` when `c` is `T` (or its subclass). One could use interface in `I` and implementation in `A`, but I want to do this without modifying `I` and `A` classes. – Suma Oct 13 '15 at 14:21

1 Answers1

2

Just replace TypeTag with ClassTag:

import scala.reflect._

object Main extends App {
  abstract class I

  class A extends I {
    def hitA() = println("Hit A")
  }

  class B extends I

  def processOpGen[T <: I : ClassTag](op: PartialFunction[I, Unit])(i: I) = {
    val fallback : PartialFunction[I, Unit] = {case _ => println("Not hit")}
    (op orElse fallback)(i)
  }

  def partialMatchGen[T <: I : ClassTag](op: T => Unit)(i: I) = {
    processOpGen[T] {
      case c: T => op(c) // can TypeTag be used here for matching somehow?
    }(i)
  }

  partialMatchGen[A](a => a.hitA())(new A)
  partialMatchGen[A](a => a.hitA())(new B)
}

Processing...
Reused last reload result
[info] Loading project definition from /tmp/rendererL3zBdh8HOA/project/project
[info] Loading project definition from /tmp/rendererL3zBdh8HOA/project
[info] Set current project to rendererWorker (in build file:/tmp/rendererL3zBdh8HOA/)
[info] Reapplying settings...
[info] Set current project to rendererWorker (in build file:/tmp/rendererL3zBdh8HOA/)
[info] Formatting 1 Scala source {file:/tmp/rendererL3zBdh8HOA/}rendererWorker(compile) ...
[warn] Scalariform parser error for /tmp/rendererL3zBdh8HOA/src/main/scala/test.scala: Expected token RBRACKET but got Token(XML_START_OPEN,<,335,<)
[info] Compiling 1 Scala source to /tmp/rendererL3zBdh8HOA/target/classes...
[success] Total time: 11 s, completed Oct 13, 2015 5:16:45 PM
Now running...
[info] Running Main 
Hit A
Not hit
[success] Total time: 0 s, completed Oct 13, 2015 5:16:45 PM
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Wow. I did not expect the solution will be this nice. I am looking for some explanation why `ClassTag` can be used for pattern matching and `TypeTag` not, but I did not find anything so far. Would you know the explanation? – Suma Oct 14 '15 at 08:29
  • 1
    I expect it's because pattern matching can't in general distinguish e.g. types with different type parameters (say `List[Int]` and `List[String]`), and allowing `TypeTag` to be used would basically promise that. – Alexey Romanov Oct 14 '15 at 16:14