开发者

Pattern matching against value with generic type

I'm writing tree-based expression evaluator, ans I've run into some troubles with type-erasure.

Tree looks like

sealed abstract class Node[+T]
case class Var[+T](name:String) extends Node[T]
/* SNIP */

The evaluator is

def eval[T](node:Node[T], context:Map[String, Any]):Option[T] = node match {
 开发者_如何学JAVA case Var(name) => context.get(name) match {
    case Some(value:T) => Some(value)
    case _ => None
  }
  /* SNIP */
}

The code compiles, but type checks on Var nodes don't work. So this test fails:

class ContextEvaluatorTest extends FunSuite with ShouldMatchers {
  test("evaluation with type mismatch") {
    ContextEvaluator.eval(Var[Int]("a"), Map("a" -> "not int")) should equal (None)
  }
}

Error message is

org.scalatest.TestFailedException: Some(not int) did not equal None

Situation looks like a use-case for manifests, but I couldn't add them properly.


This seems to work:

def eval[T:ClassManifest](node:Node[T], context:Map[String, Any]):Option[T] = node match {
  case Var(name) => context.get(name) match {
    case Some(value:T) if classManifest[T].erasure.isInstance(value) => Some(value)
    case _ => None
  }
  case _ => None
}

Note however that T must be a simple type, AFAIK ClassManifest can't distinguish between things like List[Int] and List[String]. Probably Manifestcan do that, but then the investigation is more complicated than calling isInstance on the underlying class.


It doesn't work because T is erased. That means value: T in the pattern match is meaningless. In fact, the compiler should have warned you about that.

You'll have to resort to using manifests to make that test. See Landei's answer for an example.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜