本文介绍了如何在 Scala 中测试更高级类型的类型一致性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to test whether two "containers" use the same higher-kinded type. Look at the following code:

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[_]

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

However, I expect the conformance test to succeed. What am I doing wrong? How can I test for higher-kinded types?

澄清

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

In my case, the values I am testing (the x: A in the example) come in a List[c.Expr[Any]] in a Macro. So any solution relying on static resolution (as the one I have given), will not solve my problem.

推荐答案

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

It's the mixup between underscores used in type parameter definitions and elsewhere. The underscore in TypeTag[B[_]] means an existential type, hence you get a tag not for B, but for an existential wrapper over it, which is pretty much useless without manual postprocessing.

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

Consequently typeOf[Funct[B, _]] that needs a tag for raw B can't make use of the tag for the wrapper and gets upset. By getting upset I mean it refuses to splice the tag in scope and fails with a compilation error. If you use weakTypeOf instead, then that one will succeed, but it will generate stubs for everything it couldn't splice, making the result useless for subtyping checks.

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

Looks like in this case we really hit the limits of Scala in the sense that there's no way for us to refer to raw B in WeakTypeTag[B], because we don't have kind polymorphism in Scala. Hopefully something like DOT will save us from this inconvenience, but in the meanwhile you can use this workaround (it's not pretty, but I haven't been able to come up with a simpler approach).

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 s.不幸的是,这并没有那么简单,因为 https://issues.scala-lang.org/browse/SI-7686,所以我们不得不使用弱标签,即使我们不需要.

An astute reader will notice that I used WeakTypeTag in the signature of foo, even though I should be able to use TypeTag. After all, we call foo on an Option which is a well-behaved type, in the sense that it doesn't involve unresolved type parameters or local classes that pose problems for TypeTags. Unfortunately, it's not that simple because of https://issues.scala-lang.org/browse/SI-7686, so we're forced to use a weak tag even though we shouldn't need to.

这篇关于如何在 Scala 中测试更高级类型的类型一致性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 04:58