9

我正在尝试测试两个“容器”是否使用相同的高级类型。看下面的代码:

import scala.reflect.runtime.universe._

class Funct[A[_],B]

class Foo[A : TypeTag](x: A) {
  def test[B[_]](implicit wt: WeakTypeTag[B[_]]) =
    println(typeOf[A] <:< weakTypeOf[Funct[B,_]])

  def print[B[_]](implicit wt: WeakTypeTag[B[_]]) = {
    println(typeOf[A])
    println(weakTypeOf[B[_]])
  }
}

val x = new Foo(new Funct[Option,Int])

x.test[Option]
x.print[Option]

输出是:

false
Test.Funct[Option,Int]
scala.Option[_]

但是,我希望一致性测试能够成功。我究竟做错了什么?如何测试更高种类的类型?

澄清

就我而言,我正在测试的值(x: A在示例中)出现在List[c.Expr[Any]]宏中。因此,任何依赖静态分辨率的解决方案(如我给出的解决方案)都无法解决我的问题。

4

2 回答 2

8

这是类型参数定义和其他地方使用的下划线之间的混淆。中的下划线TypeTag[B[_]]表示存在类型,因此您得到的标签不是 for B,而是针对它的存在包装器,如果没有手动后处理,这几乎没有用。

因此typeOf[Funct[B, _]],需要一个原始标签B不能使用包装器的标签并感到不安。生气是指它拒绝在范围内拼接标签并因编译错误而失败。如果你weakTypeOf改用,那么那个会成功,但它会为所有无法拼接的东西生成存根,使结果对子类型检查无用。

看起来在这种情况下,我们真的达到了 Scala 的极限,因为我们无法在 Scala 中引用 raw BWeakTypeTag[B]因为我们在 Scala 中没有善良的多态性。希望像DOT这样的东西可以让我们免于这种不便,但与此同时,您可以使用这种解决方法(它并不漂亮,但我无法想出更简单的方法)。

import scala.reflect.runtime.universe._

object Test extends App {
  class Foo[B[_], T]
  // NOTE: ideally we'd be able to write this, but since it's not valid Scala
  // we have to work around by using an existential type
  // def test[B[_]](implicit tt: WeakTypeTag[B]) = weakTypeOf[Foo[B, _]]
  def test[B[_]](implicit tt: WeakTypeTag[B[_]]) = {
    val ExistentialType(_, TypeRef(pre, sym, _)) = tt.tpe

    // attempt #1: just compose the type manually
    // but what do we put there instead of question marks?!
    // appliedType(typeOf[Foo], List(TypeRef(pre, sym, Nil), ???))

    // attempt #2: reify a template and then manually replace the stubs
    val template = typeOf[Foo[Hack, _]]
    val result = template.substituteSymbols(List(typeOf[Hack[_]].typeSymbol), List(sym))
    println(result)
  }
  test[Option]
}

// has to be top-level, otherwise the substituion magic won't work
class Hack[T]

精明的读者会注意到我WeakTypeTag在签名中使用了foo,即使我应该可以使用TypeTag。毕竟,我们在一个Option行为良好的类型上调用 foo,因为它不涉及未解析的类型参数或对TypeTags 造成问题的本地类。不幸的是,由于https://issues.scala-lang.org/browse/SI-7686 ,它并不是那么简单,所以即使我们不需要,我们也被迫使用弱标签。

于 2013-07-22T15:50:30.633 回答
5

以下是适用于我给出的示例的答案(并且可能对其他人有所帮助),但不适用于我的(非简化)案例。

从@pedrofurla 的提示中窃取,并使用类型类:

trait ConfTest[A,B] {
  def conform: Boolean
}

trait LowPrioConfTest {
  implicit def ctF[A,B] = new ConfTest[A,B] { val conform = false }
}

object ConfTest extends LowPrioConfTest {
  implicit def ctT[A,B](implicit ev: A <:< B) =
    new ConfTest[A,B] { val conform = true }
}

并将其添加到Foo

def imp[B[_]](implicit ct: ConfTest[A,Funct[B,_]]) =
  println(ct.conform)

现在:

x.imp[Option] // --> true
x.imp[List]   // --> false
于 2013-07-21T18:46:57.420 回答