【问题标题】:How to test type conformance of higher-kinded types in Scala如何在 Scala 中测试高级类型的类型一致性
【发布时间】:2013-07-19 11:05:11
【问题描述】:

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

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]] 中。所以任何依赖静态分辨率的解决方案(就像我给出的那样)都不能解决我的问题。

【问题讨论】:

  • 支持您的期望:implicitly[Funct[Option,Int] &lt;:&lt; Funct[Option,_]]
  • 来自gist.github.com/xeno-by/6054650 我可以看到类型修饰符没有看到 B 的类型标记,所以你没有从weakTypeOf[Funct[B, _]] 中得到正确的类型。
  • 好的,现在我知道问题出在哪里了。这又是类型参数定义和其他地方使用的下划线之间的混淆。 TypeTag[B[_]] 中的下划线表示存在类型,因此您得到的类型标记不是B,而是存在于它的包装器。因此typeOf[Funct[B, _]] 无法使用此类型标签并感到不安。
  • @EugeneBurmako 对我来说非常有意义。但是我不允许写WeakTypeTag[B]B带类型参数)。此外,weakTypeOf[Funct[B, _]]确实使用了WeakTypeTag。如果我不提供隐含的WeakTypeTag[B[_]],它将失败。
  • 发布了带有解决方法的答案。

标签: scala reflection types


【解决方案1】:

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

因此,需要原始标签的typeOf[Funct[B, _]] 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,这是一种行为良好的类型,因为它不涉及未解析的类型参数或给 TypeTags 带来问题的本地类。不幸的是,因为https://issues.scala-lang.org/browse/SI-7686,事情并没有那么简单,所以我们不得不使用弱标签,即使我们不需要这样做。

【讨论】:

  • 可以对 Foo 进行抽象吗?
【解决方案2】:

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

从@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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2023-03-30
    • 2013-09-15
    • 2017-09-12
    • 2015-03-08
    • 1970-01-01
    相关资源
    最近更新 更多