通过IMain在Scala REPL中获取类型信息

8

意图

我正在尝试添加:kind命令的支持到 repl。感谢Eugene Burmako,我能够获得一个可工作的原型。虽然它只适用于完全限定名称并且无法解析导入的名称。

我现在正在尝试使用IMain.exprTyper来完成这项工作,因为它了解类型并被导入到repl中。但是有一个问题。我尝试过的所有方法都返回一个ClassInfoType,如下所示(使用showRaw显示):

ClassInfoType(List(TypeRef(TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package java, List()), package lang, List()), class Object, List()), TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package scala, List()), trait Serializable, List())), Scope{
  def <init>(): Option.type;
  implicit def option2Iterable(xo: Option): Iterable;
  def apply(x: Object): Option;
  def empty(): Option;
  private def readResolve(): Object
}, object Option)

虽然工作实现返回特定的Type

PolyType(List(TypeName("A")), ClassInfoType(List(TypeRef(ThisType(scala), TypeName("AnyRef"), List()), TypeRef(ThisType(scala), scala.Product, List()), TypeRef(ThisType(scala), scala.Serializable, List())), Scope(nme.CONSTRUCTOR, TermName("isEmpty"), TermName("isDefined"), TermName("get"), TermName("getOrElse"), TermName("orNull"), TermName("map"), TermName("fold"), TermName("flatMap"), TermName("flatten"), TermName("filter"), TermName("filterNot"), TermName("nonEmpty"), TermName("withFilter"), TypeName("WithFilter"), TermName("contains"), TermName("exists"), TermName("forall"), TermName("foreach"), TermName("collect"), TermName("orElse"), TermName("iterator"), TermName("toList"), TermName("toRight"), TermName("toLeft")), scala.Option))

问题

我觉得我离成功很近了。这里有一个游乐场,你可以用它来尝试一切:

Welcome to Scala version 2.11.0-20130328-093148-47645c7e7e (OpenJDK 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> import scala.tools.nsc.interpreter.IMain
import scala.tools.nsc.interpreter.IMain

scala> val mirror = runtimeMirror(getClass.getClassLoader) // Working approach
mirror: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@3d34ec98 of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [<unknown>] and parent being scala.tools.nsc.util.ScalaClassLoader$URLClassLoader@5d990e8c of type class scala.tools.nsc.util.ScalaClassLoader$URLClassLoader with classpath [file:/usr/lib/jvm/java-7-openjdk/jre/lib/resources.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/rt.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/jsse.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/jce.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/charsets.jar,file:/usr/lib/jvm/java-7-openjdk/jre/lib/rhino.jar,file:/home/folone/workspace/scala-myfork/build/pack/lib/jline.jar,file:/home/folone/workspace/scala-myfork...

scala> val typer = new IMain().exprTyper // Not working approach
typer: scala.tools.nsc.interpreter.IMain#exprTyper.type = scala.tools.nsc.interpreter.IMain$exprTyper$@68c181f0

scala> val expr = "scala.Option"
expr: String = scala.Option

scala> showRaw(mirror.staticClass(expr).toType.typeSymbol.typeSignature) // Correct signature
res6: String = PolyType(List(TypeName("A")), ClassInfoType(List(TypeRef(ThisType(scala), TypeName("AnyRef"), List()), TypeRef(ThisType(scala), scala.Product, List()), TypeRef(ThisType(scala), scala.Serializable, List())), Scope(nme.CONSTRUCTOR, TermName("isEmpty"), TermName("isDefined"), TermName("get"), TermName("getOrElse"), TermName("orNull"), TermName("map"), TermName("fold"), TermName("flatMap"), TermName("flatten"), TermName("filter"), TermName("filterNot"), TermName("nonEmpty"), TermName("withFilter"), TypeName("WithFilter"), TermName("contains"), TermName("exists"), TermName("forall"), TermName("foreach"), TermName("collect"), TermName("orElse"), TermName("iterator"), TermName("toList"), TermName("toRight"), TermName("toLeft")), scala.Option))

scala> showRaw(typer.typeOfExpression(expr).typeSymbol.typeSignature) // Wrong signature
res7: String = 
ClassInfoType(List(TypeRef(TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package java, List()), package lang, List()), class Object, List()), TypeRef(TypeRef(TypeRef(NoPrefix(), package <root>, List()), package scala, List()), trait Serializable, List())), Scope{
  def <init>(): Option.type;
  implicit def option2Iterable(xo: Option): Iterable;
  def apply(x: Object): Option;
  def empty(): Option;
  private def readResolve(): Object
}, object Option)

我如何将ClassInfoType转换为包含所需信息的有效Type?或者,我如何在第一次使用IMain时获取Type
2个回答

5

这样怎么样?我正在使用 power mode,它可以让你从当前运行的 REPL 中访问全局变量,比创建一个新的 IMain 更加方便。

scala> :power
Already in power mode.

scala> val g = global
g: $r.intp.global.type = <global>

scala> val context = g.analyzer.rootContext(NoCompilationUnit)
context: g.analyzer.Context = Context(<root>@EmptyTree unit=NoCompilationUnit scope=997093283 errors=false, reportErrors=true, throwErrors=false)

// aware imports of scala._, etc.
scala> val sym = context.lookupSymbol("Option": TypeName, _ => true).symbol
sym: g.analyzer.global.Symbol = class Option

scala> sym.tpeHK.typeParams
res21: List[g.analyzer.global.Symbol] = List(type A)

参见:

scala> intp.symbolOfType("Foo")
res26: $r.intp.global.Symbol = class Foo

但是我不确定如何获取之前导入的符号:

scala> object Bar { class Bop }
defined object Bar

scala> import Bar.Bop
import Bar.Bop

scala> intp.symbolOfType("Bop")
res27: $r.intp.global.Symbol = <none>

编辑:

OP之所以得到ClassInfoType而不是PolyType,是由于阶段的原因。要获得与强制模式的global相同的结果,必须将阶段设置为typer。引用@retronym在REPL:intp.global vs“:power mode”中可用的“global”的解释:

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
    ^
    `----  this part is relevant

符号具有类型列表(也称为info),按编译器阶段索引(即TypeHistory)。许多编译器阶段都会安装InfoTransformer来转换类型。请参阅src/compiler/scala/tools/nsc/transform/InfoTransform.scala以获取一些文档。

要检查特定阶段的类型,可以使用像“exitingTyper”这样的方法。

scala> exitingPostErasure($intp.global.rootMirror.staticClass("scala.Option").typeSignature).getClass
res6: Class[_ <: $intp.global.Type] = class scala.reflect.internal.Types$ClassInfoType

scala> exitingTyper($intp.global.rootMirror.staticClass("scala.Option").typeSignature).getClass
res7: Class[_ <: $intp.global.Type] = class scala.reflect.internal.Types$PolyType

或者,更为方便的是,在 :power 模式下:
scala> :phase typer
Active phase is now: Typer

scala> global.rootMirror.staticClass("scala.Option").typeSignature.getClass
res16: Class[_ <: $r.global.Type] = class scala.reflect.internal.Types$PolyType

scala> :phase cleanup
Active phase is now: Cleanup

scala> global.rootMirror.staticClass("scala.Option").typeSignature.getClass
res17: Class[_ <: $r.global.Type] = class scala.reflect.internal.Types$ClassInfoType

这看起来非常奇怪。我在实际代码(https://github.com/folone/scala/blob/master/src/repl/scala/tools/nsc/interpreter/package.scala#L130-L169)中尝试过了,但它没有起作用:仍然从“typeSig”方法获取“ClassInfoType”。但当我将其复制粘贴到repl中时,它起作用了(至少对于“Option”而言): https://gist.github.com/folone/46326582337d7fbaee51 - George
无论如何,看起来我通过将您的建议与之前部分工作的东西相结合找到了一个可行的解决方案:https://github.com/folone/scala/commit/fe6720c00affc08b5e83e8c2cb1c81139521da4e 非常感谢您的帮助! - George
我根据@retronym在scala-internal上的回答,添加了关于阶段的关键信息。 - Eugene Yokota

2

您应该使用 IMain 全局中的镜像:

scala> val imain = new IMain()
imain: scala.tools.nsc.interpreter.IMain = scala

scala> val mirror = imain.global.rootMirror
mirror: imain.global.Mirror = compiler mirror

我刚试了一下。showRaw(mirror.staticClass("scala.Option").typeSignature)仍然显示为ClassInfoType。而且它也无法解析Option(不带完全限定名)。 - George

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接