我有以下特征(获得排名2多态性点击)
type Id[A] = A
trait ~>[F[_], G[_]] {
def apply[A](a: F[A]): G[A]
def isDefinedAt[A](a: A): Boolean}
和将部分函数转换为该特征的函数:
implicit def pft[B: ClassTag](f: PartialFunction[B, B])= new (Id ~> Id) {
def apply[A](a: A): A = f(a.asInstanceOf[B]).asInstanceOf[A]
def isDefinedAt[A: ClassTag](a: A)(implicit ev2: ClassTag[A]) : Boolean = /*type check*/
f.isDefinedAt(a.asInstanceOf[B]) }
我的问题是isDefinedAt方法。我必须在运行时检查A是否是B的实例。a.isInstanceOf[B]由于类型擦除而不能工作
我尝试使用TypeTag/ClassTag和B,工作得很好,但A的类型总是Any.
那么,我如何检查A是否是B的实例呢?
更新:我在这个代码中使用它:
def map[A](f: Id ~> Id, x: A): A =
{
val y = x match {
//many matches more on my own data structures
case l : List[_] => l.map(map(f,_))
case m : Map[_,_] => m.map(map(f,_))
case (a,b) => (map(f,a),map(f,b))
case a => a
}
if (f.isDefinedAt(y))
f(y).asInstanceOf[A]
else
y.asInstanceOf[A]
}
如果我直接使用~>,一切都可以正常使用typeTag[A]。tpe & lt;: & lt;类型标签[B] .tpe。
但是如果我将它与这个映射函数typeTag[A]一起使用。
如果您同意修改isDefinedAt的签名以接受TypeTag,则可以这样实现pft:
type Id[A] = A
trait ~>[F[_], G[_]] {
def apply[A](a: F[A]): G[A]
def isDefinedAt[A: TypeTag](a: A): Boolean
}
implicit def pft[B: TypeTag](f: PartialFunction[B, B]) = new (Id ~> Id) {
def apply[A](a: A): A = f(a.asInstanceOf[B]).asInstanceOf[A]
def isDefinedAt[A: TypeTag](a: A): Boolean =
typeTag[A].tpe =:= typeTag[B].tpe && f.isDefinedAt(a.asInstanceOf[B])
}
此解决方案获得B
和A
的TypeTag,并在委托给部分函数的isDefinedAt
方法之前验证它们是相同类型。有关类型标签的更多信息,请参阅此回答。
val halfEven: PartialFunction[Int, Int] = {
case n if n % 2 == 0 => n / 2
}
val halfEvenT: Id ~> Id = halfEven
halfEvenT.isDefinedAt(1) // false
halfEvenT.isDefinedAt(2) // true
halfEvenT.isDefinedAt("test") // false
更一般地说,您可以将受约束的部分转换定义为采用约束A
的附加高级类型:
trait ConstrainedPartialTransformation[F[_], G[_], C[_]] {
def apply[A: C](a: F[A]): G[A]
def isDefinedAt[A: C](a: A): Boolean
}
implicit def cpft[B: TypeTag](f: PartialFunction[B, B]) = new ConstrainedPartialTransformation[Id, Id, TypeTag] {
def apply[A: TypeTag](a: A) = f(a.asInstanceOf[B]).asInstanceOf[A]
def isDefinedAt[A: TypeTag](a: A) =
typeTag[A].tpe =:= typeTag[B].tpe && f.isDefinedAt(a.asInstanceOf[B])
}
这类似于Scalaz 7支持自然转换的方式,其中有一个上下文绑定在a上。