当包对象扩展特征时,WeakTypeTag不起作用

时间:2014-08-31 15:39:23

标签: scala

WeakTypeTag s:

来考虑这个人为的例子
import reflect.runtime.universe.WeakTypeTag
import reflect.runtime.universe.weakTypeOf

package object stackoverflow1 {
  sealed trait Status
  trait Open extends Status
  trait Closed extends Status

  case class Buffer[S <: Status](available: Int)
  object Buffer {
    def create(available: Int): Buffer[Open] = new Buffer[Open](available)
    def from[S <: Status: WeakTypeTag](b: Buffer[S]): Buffer[Open] = {
      weakTypeOf[S] match {
        case t if t <:< weakTypeOf[Open]   => b.asInstanceOf[Buffer[Open]]
        case t if t <:< weakTypeOf[Closed] => new Buffer[Open](b.available)
      }
    }
  }
}

package stackoverflow1 {
  object Main extends App {
    val b1 = Buffer.create(1024)
    val b2 = Buffer.from(b1)
  }
}

这很好用,但是当我将包对象的代码移动到特征中并将该特征混合到包对象中时,它不再起作用了:

import reflect.runtime.universe.WeakTypeTag
import reflect.runtime.universe.weakTypeOf

package stackoverflow2 {
  trait Mixin {
    sealed trait Status
    trait Open extends Status
    trait Closed extends Status

    case class Buffer[S <: Status](available: Int)
    object Buffer {
      def create(available: Int): Buffer[Open] = new Buffer[Open](available)
      def from[S <: Status: WeakTypeTag](b: Buffer[S]): Buffer[Open] = {
        weakTypeOf[S] match {
          case t if t <:< weakTypeOf[Open]   => b.asInstanceOf[Buffer[Open]]
          case t if t <:< weakTypeOf[Closed] => new Buffer[Open](b.available)
        }
      }
    }
  }
}

package object stackoverflow2 extends Mixin

package stackoverflow2 {
  object Main extends App {
    val b1 = Buffer.create(1024)
    val b2 = Buffer.from(b1)
  }
}

的ErrorMessage:
Exception in thread "main" scala.MatchError: stackoverflow2.Open (of class scala.reflect.internal.Types$ClassNoArgsTypeRef)?

一些想法为什么?

编辑

使用抽象类型成员而不是类型参数:

import reflect.runtime.universe._

package stackoverflow3 {
  trait Mixin {
    sealed trait Status
    trait Open extends Status
    trait Closed extends Status

    case class Buffer(available: Int) {
      type S <: Status
      def withStatus[S <: Status] = asInstanceOf[BufferAux[S]]
    }
    type BufferAux[S0 <: Status] = Buffer { type S = S0 }
    object Buffer {
      def create(available: Int): BufferAux[Open] = Buffer(available).withStatus[Open]
      def from[S <: Status: WeakTypeTag](b: BufferAux[S]): BufferAux[Open] = {
        weakTypeOf[S] match {
          case t if t <:< weakTypeOf[Open]   => b.asInstanceOf[BufferAux[Open]]
          case t if t <:< weakTypeOf[Closed] => Buffer(b.available).withStatus[Open] // no need for creating new Buffer, only shows intend
        }
      }
    }
  }
}

package object stackoverflow3 extends Mixin

package stackoverflow3 {
  object Main extends App {
    val b1 = Buffer.create(1024)
    val b2 = Buffer.from(b1)
  }
}

1 个答案:

答案 0 :(得分:3)

让我们为Buffer.from方法添加一些调试打印:

import scala.reflect.runtime.universe.showRaw
println(s"scrut = ${weakTypeOf[S]}, ${showRaw(weakTypeOf[S])}")
println(s"Open = ${weakTypeOf[Open]}, ${showRaw(weakTypeOf[Open])}")
println(s"Closed = ${weakTypeOf[Closed]}, ${showRaw(weakTypeOf[Closed])}")

这将打印出有问题的类型及其结构。也许这可以解释发生了什么:

scrut = stackoverflow2.Open, TypeRef(SingleType(ThisType(stackoverflow2), stackoverflow2.package), TypeName("Open"), List())
Open = Mixin.this.Open, TypeRef(ThisType(stackoverflow2.Mixin), TypeName("Open"), List())
Closed = Mixin.this.Closed, TypeRef(ThisType(stackoverflow2.Mixin), TypeName("Closed"), List())

好吧,这显示了相关类型的完全限定版本,并揭示了可能存在问题的差异。因此,反思说stackoverflow2.Open不是Mixin.this.Open的子类型。嗯,但包对象stackoverflow2扩展Mixin。这一定是反思中的错误,对吧?

让我们问问第三方并检查scalac对此有何评论。我们在val test: Mixin.this.Open = (??? : stackoverflow2.Open)内编写类似Buffer.from的内容并尝试编译?

Test.scala:17: error: type mismatch;
 found   : stackoverflow2.Open
 required: Mixin.this.Open
        val test: Open = (??? : stackoverflow2.Open)
                              ^
one error found

好吧,scalac实际上同意反思,所以也许这不是一个错误。它确实不是错误,因为如果我们创建另一个Mixin的子类,那么它将拥有自己的Mixin.this.Open副本,这将与stackoverflow2的{​​{1}}副本不兼容{1}}。这意味着Mixin.this.Open理所当然不是stackoverflow2.Open的子类型。

我们在这里有几个选择。

首先,我们可以写Mixin.this.Open而不是t <:< weakTypeOf[Open]。不漂亮,因为这会将有关子类的知识放入超类中。

其次,我们可以使用t <:< weakTypeOf[stackoverflow2.Open]上的asSeenFromweakTypeOf[Open]调整为传递给Mixin.this.Open的类型的容器。这将安抚子类型检查器,因为它会将一棵树上的苹果与同一棵树上的苹果进行比较,可以这么说:

from

第三,我们可以进行手动检查以验证:a)我们传递的类型是在type InternalType = scala.reflect.internal.SymbolTable#Type type ApiType = Type val pre = weakTypeOf[S].asInstanceOf[InternalType].prefix.asInstanceOf[ApiType] val topen = weakTypeOf[Open].asSeenFrom(pre, symbolOf[Mixin]) ... case t if t <:< topen => b.asInstanceOf[Buffer[Open]] ... 的子类型中声明的,b)基础符号的名称是Mixin或者Open。这似乎比第二个选项更容易,但实际上我不建议去做,因为当你自己重新实现这样的东西时,很容易忽略一些角落的情况。例如,上面提供的配方实际上是错误的,因为除了a)和b)之外,我们还需要检查c)底层符号是从Closed继承而不是重新定义(即阴影)。我可能会忽略其他一些东西,所以第三种选择绝对是风险最大的选择。