匹配两个ADT

时间:2015-10-07 13:27:55

标签: scala pattern-matching

我有两个ADT结构。

trait Values[_] {}
case class StringV(values: List[String]) extends Values[String]
case class NumberV(values: List[Double]) extends Values[Double]
// and a few more
trait Filter[_] {}
case class StringFilter(values: Set[String]) extends Filter[String]
case class NumberFilter(lower: Double, upper: Double) extends Filter[Double]
// and a few more

给出两个变量

val v: Values[_]
val f: Filter[_]

如何在运行时检查它们是否具有相同的类型,并恢复if 他们不?模式匹配不起作用。

(v, f) match {
  case (v: Values[A], f: Filter[A]) forSome { type A } => 
}

蛮力解决方案

def typecheck(v: Values[_], f: Filter[_]):
  Option[(Values[A], Filter[A]) forSome { type A }] =
  (v, f) match {
    case v: StringV => f match {
      case f: StringFilter => Some(v, f)
      case _ => None
    }
    case v: NumberV => f match {
      case f: NumberFilter => Some(v, f)
      case _ => None
    }
  }

有没有比简单地输入所有内容更好的解决方案?

编辑:

我希望元组(v, f)符合类型(Values[A], ParamFilterConfig[A]) forSome { type A }

1 个答案:

答案 0 :(得分:1)

您可以使用反射API中的typeOf

import reflect.runtime.universe._

def valueAndFilter[V: TypeTag, F: TypeTag](v: Values[V], f: Filter[F]) =
  if (typeOf[V] =:= typeOf[F]) Some(v, f) else None

valueAndFilter(NumberV(1 :: Nil), NumberFilter(1d, 2d))
// Some((NumberV(List(1.0)),NumberFilter(1.0,2.0)))
valueAndFilter(StringV(Nil), NumberFilter(1d, 2d))
// None

您还可以在编译时通过请求隐式证据来检查ValuesFilter的类型是否相等。

def valueAndFilter2[V, F](v: Values[V], f: Filter[F])(implicit ev: V =:= F) = (v, f)

valueAndFilter2(NumberV(1 :: Nil), NumberFilter(1d, 2d))
// (NumberV(List(1.0)),NumberFilter(1.0,2.0))
valueAndFilter2(StringV(Nil), NumberFilter(1d, 2d))
// error: Cannot prove that String =:= Double.