1
votes

Given the following type and instance:

type operation = (Int, Int) => Int
def add: operation = _ + _

If I try to match an operation in a case statement, Scala complains about unchecked typing due to type erasure:

for (a <- elements) a match {
  case o: operation => // do stuff
}

Is there a way to achieve this kind of function-based typing while being erasure-friendly in case statements?

Note, this is similar to this thread.

2
Note that this question isn't particularly like the other linked thread because that one doesn't involve type erasure.James Iry

2 Answers

7
votes

One easy way to deal with type erasure is to create an unparamaterized class. It's not perfect, but it works. Make it a case class that extends Function2 and it's not even too clunky to use either directly or in a pattern match

scala> case class Operation(f : (Int,Int) => Int) extends ((Int,Int) => Int) {
     | def apply(x : Int, y : Int) = f(x,y)                                   
     | }
defined class Operation

scala> def add = Operation(_ + _)                                             
add: Operation

scala> val y = add(7,3)
y: Int = 10

scala> val elements = List(1, add, 2)
elements: List[Any] = List(1, <function2>, 2)

scala> for (a <- elements) yield a match {        
     | case Operation(f) => f(1,2)        
     | case x : Int => x
     | }
res0: List[Int] = List(1, 3, 2)

The limitation is that you have to have "boxed" the operation before you lose its type, not after. Also, you end up with one class per concrete function type.

Another, arguably much better, solution is to not lose the type information. Use an Either to retain the static type info.

scala> val elements : List[Either[Int, (Int, Int) => Int]] = List(Left(1), Right(_ + _), Left(2))
elements: List[Either[Int,(Int, Int) => Int]] = List(Left(1), Right(<function2>), Left(2))

scala> for (a <- elements) yield a match {
     | case Right(f) => f(1,2)            
     | case Left(x) => x                  
     | }

res1: List[Int] = List(1, 3, 2)

The limitation here is that it gets clunky if your List can have more than 2 types. But it effectively avoids forcing Scala to be a dynamically typed language, unlike the previous solution.

0
votes

If you can wrap a into an Option, then this will work:

scala> val a:Option[Any] = Some(add)
a: Option[Any] = Some(<function2>)

scala> a match { case o:Some[operation] => println ("found"); case _ => }
found