我试图测试两个“容器”是否使用相同的高级类型。请查看以下代码:
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]]
。所以任何依赖于静态分辨率的解决方案(就像我给出的那样)都无法解决我的问题。
答案 0 :(得分:8)
这是类型参数定义和其他地方使用的下划线之间的混合。 TypeTag[B[_]]
中的下划线表示存在类型,因此您获得的标记不是B
,而是存在于它上面的存在包装,如果没有手动后处理,这几乎是无用的。
因此需要原始typeOf[Funct[B, _]]
标记的B
无法使用包装器的标记而感到沮丧。令我感到沮丧的意思是它拒绝在范围内拼接标签并因编译错误而失败。如果你改为使用weakTypeOf
,那么它会成功,但它会为它无法拼接的所有内容生成存根,使得结果对于子类型检查没用。
在这种情况下,我们真的达到了Scala的极限,因为我们无法在B
中引用原始WeakTypeTag[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,这是一个表现良好的类型,因为它不涉及未解析的类型参数或为TypeTag
带来问题的本地类。不幸的是,由于https://issues.scala-lang.org/browse/SI-7686,它并不那么简单,所以即使我们不需要,我们也不得不使用弱标签。
答案 1 :(得分: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