如何在Scala中测试高级类型的类型一致性

gzm*_*zm0 9 reflection types scala

我试图测试两个"容器"是否使用相同的高级类型.看下面的代码:

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]
Run Code Online (Sandbox Code Playgroud)

输出是:

false
Test.Funct[Option,Int]
scala.Option[_]
Run Code Online (Sandbox Code Playgroud)

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

澄清

在我的例子中,我正在测试的值(x: A在示例中)来自List[c.Expr[Any]]宏.所以任何依赖于静态分辨率的解决方案(就像我给出的那样)都无法解决我的问题.

Eug*_*ako 8

它是类型参数定义中使用的下划线与其他地方之间的混合.在下划线TypeTag[B[_]]是指存在类型,所以你不是一个标签B,而是一种存在包装过它,这是无需人工后处理几乎无用.

因此typeOf[Funct[B, _]]需要raw的标签B不能使用包装器的标签而感到沮丧.令我感到沮丧的意思是它拒绝在范围内拼接标签并因编译错误而失败.如果你使用weakTypeOf,那么那个会成功,但它会为它无法拼接的所有东西生成存根,使得结果对于子类型检查没用.

貌似在这种情况下,我们真的打斯卡拉的界限在这个意义上,有没有办法让我们来指代生BWeakTypeTag[B],因为我们没有那种多态性斯卡拉.希望像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]
Run Code Online (Sandbox Code Playgroud)

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


gzm*_*zm0 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 }
}
Run Code Online (Sandbox Code Playgroud)

并将其添加到Foo:

def imp[B[_]](implicit ct: ConfTest[A,Funct[B,_]]) =
  println(ct.conform)
Run Code Online (Sandbox Code Playgroud)

现在:

x.imp[Option] // --> true
x.imp[List]   // --> false
Run Code Online (Sandbox Code Playgroud)