繁体   English   中英

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

[英]How to test type conformance of higher-kinded types in 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]

输出是:

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

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

澄清

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

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

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

看起来在这种情况下我们真的达到了Scala的极限,因为我们无法在WeakTypeTag[B]引用原始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]

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

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

窃取@ 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM