I'm rather confused about what's happening here:
import scala.collection.immutable._
object Main extends App {
  sealed trait Node
  sealed trait Group
  case class Sheet(
    val splat: String,
    val charname: String, 
    val children: ListMap[String, Node],
    val params0: ListMap[String, Param], //params0 to separate sheet-general parameters
    val note: Option[Note]
    ) extends Node with Group
  case class Attributes(val name: String) extends Node with Group
  case class Param(val name: String, val value: String) extends Node
  case class Note(val note: String) extends Node
I've got three versions of a replace function - the last one is the one I'm actually trying to write, the others are just debugging.
  class SheetUpdater(s: Sheet) {    
    def replace1[T <: Group](g: T): Unit = {
      s.children.head match {
        case (_, _:Sheet) =>
        case (_, _:Attributes) =>
      }
    }
  }
This version throws no warnings, so apparently I have access to the type of s.children at runtime.
  class SheetUpdater(s: Sheet) {
    def replace2[T <: Group](g: T): Unit = {
      g match {
        case _:Sheet =>
        case _:Attributes =>
      }
    }
  }
Neither does this version, so apparently the details of g's type are also available at runtime...
  class SheetUpdater(s: Sheet) {
    def replace3[T <: Group](g: T): Unit = {
      s.children.head match {
        case (_, _:T) => //!
        case (_, _:Attributes) =>
      }
    }
  }
... but even so, this ends up throwing me the dreaded Abstract type pattern T is unchecked since it is eliminated by erasure warning. What's going on here?
In Scala, generics are erased at runtime, which means that the runtime type of List[Int] and List[Boolean] is actually the same. This is because the JVM as a whole erases generic types. All this is due because the JVM wanted to remain backwards compatible way back when generics were first introduced...
There is a way around this in Scala using a ClassTag, which is an implicit parameter that then can be threaded around with whatever generic you are using.
You can think of : ClassTag as passing the type of the generic as an argument. (It is syntactic sugar for passing an implicit parameter of type ClassTag[T].)
import scala.reflect.ClassTag
class SheetUpdater(s: Sheet) {
  def replace3[T <: Group : ClassTag](g: T): Unit = {
    s.children.head match {
      case (_, _:T) => //!
      case (_, _:Attributes) =>
    }
  }
}
Newer answers of this question have more details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With