如何调查对象/types/等。 来自Scala REPL?
我已经和Scala一起工作了一段时间,并且写了一个超过10,000个的线程程序,但是我仍然对一些内部工作感到困惑。 在和Java,C和Lisp熟悉之后,我来到了Python的Scala,但是即使如此,它也一直很慢,而且一个巨大的问题是我试图调查对象/types的内部工作时经常遇到的令人沮丧的难题/类/等。 与Python相比,使用Scala REPL。 在Python中,您可以使用foo
调查任何对象foo
(types,全局variables中的对象,内置函数等),以查看该事物的计算结果, type(foo)
以显示其types,指示dir(foo)
你可以调用它的方法,并help(foo)
获取内置的文档。 即使没有与之关联的对象,甚至可以执行诸如help("re")
来find名为re
的包(包含正则expression式对象和方法)的文档。
在Scala中,您可以尝试在线阅读文档,查看源代码到图书馆等等,但是对于不知道在哪里甚至是在什么地方的东西(通常是很常见的一个庞大的大块咬,考虑到大量的types层次结构) – 东西在各个地方(包scala
, Predef
,各种隐式转换,像::
这样的符号几乎是不可能的谷歌)浮动。 REPL应该是直接探索的方式,但事实上,事情更加神秘。 假设我已经在某处看到了某个引用,但是我不知道它是什么。 显然没有“用REPL系统地调查Scala东西的指南”这样的东西,但是下面是我经过大量的反复试验后拼凑起来的东西:
- 如果
foo
是一个值(大概包含存储在variables加上伴随对象和其他Scalaobject
的东西),那么可以直接评估foo
。 这应该告诉你结果的types和价值。 有时结果是有帮助的,有时候不是。 - 如果
foo
是一个值,则可以使用:type foo
来获取其types。 (不一定有启发性。)如果你在函数调用中使用它,你将得到返回值的types,而不用调用函数。 - 如果
foo
是一个值,你可以使用foo.getClass
来获得它的类。 (通常比以前更有启发性,但是一个对象的类与其types有什么不同?) - 对于一个类
foo
,可以使用classOf[foo]
,虽然结果的含义并不明显。 - 从理论上讲,你可以使用
:javap foo
来反汇编一个类 – 这应该是最有用的,但是对于我来说却完全失败了。 - 有时你必须从错误消息中把东西拼凑起来。
失败的例子使用:javap
:
scala> :javap List Failed: Could not find class bytes for 'List'
启发错误信息示例:
scala> assert <console>:8: error: ambiguous reference to overloaded definition, both method assert in object Predef of type (assertion: Boolean, message: => Any)Unit and method assert in object Predef of type (assertion: Boolean)Unit match expected type ? assert ^
好的,现在我们来看一个简单的例子。
scala> 5 res63: Int = 5 scala> :type 5 Int scala> 5.getClass res64: java.lang.Class[Int] = int
够简单了…
现在,让我们尝试一些真实的情况,其中并不那么明显:
scala> Predef res65: type = scala.Predef$@3cd41115 scala> :type Predef type scala> Predef.getClass res66: java.lang.Class[_ <: object Predef] = class scala.Predef$
这是什么意思? 为什么Predef
的types只是简单的type
,而类是scala.Predef$
? 我认为,$是伙伴对象被爪哇的方式……但斯卡拉在Google上的文档告诉我, Predef
是object Predef extends LowPriorityImplicits
– 我怎么能从REPL推导出这个? 我怎么看看里面有什么?
好吧,让我们试试另一个令人困惑的事情:
scala> `::` res77: collection.immutable.::.type = :: scala> :type `::` collection.immutable.::.type scala> `::`.getClass res79: java.lang.Class[_ <: object scala.collection.immutable.::] = class scala.collection.immutable.$colon$colon$ scala> classOf[`::`] <console>:8: error: type :: takes type parameters classOf[`::`] ^ scala> classOf[`::`[Int]] res81: java.lang.Class[::[Int]] = class scala.collection.immutable.$colon$colon
好吧,这让我无可奈何的困惑,最后我不得不阅读源代码来理解这一切。
所以,我的问题是:
- 从真正的Scala专家那里推荐的最好的方法是使用REPL来理解Scala对象,类,方法等等,或者至less从REPL可以做到最好的调查它们?
- 我如何得到
:javap
从REPL的:javap
工作内置的东西? (它不应该默认工作?)
感谢任何启示。
你提到了Scala缺乏的一个重要点:文档。
REPL是一个很棒的工具,但它不是那么棒。 有太多遗漏的function和function可以改进 – 其中一些在您的文章中提到。 Scaladoc也是一个不错的工具,但是很远很完美。 此外,API中的许多代码还没有或没有太多的文档logging,代码示例经常被忽略。 IDE是完整的obb错误,与Java IDE向我们展示的看起来像是一些幼儿园玩具的可能性相比。
尽pipe如此,与我在2 – 3年前开始学习Scala时所用的工具相比,斯卡拉当前的工具有着巨大的差异。 当时IDE在后台永久编译了一些垃圾,编译器每隔几分钟就会崩溃,一些文档绝对不存在。 我经常受到愤怒的攻击,希望Scala作者死亡和腐败。
现在? 我没有任何这些愤怒的攻击了。 因为我们目前所拥有的工具虽然不完美,但却很棒!
有docs.scala-lang.org ,它总结了很多很棒的文档。 有教程,作弊表,词汇表,指南和很多更好的东西。 另一个伟大的工具是Scalex ,它甚至可以find人们能想到的最奇怪的算子。 这是Scalas Hoogle ,虽然还不如他的伟大理想,但它是非常有用的。
Scala2.10以Scala自带的Reflection库的forms出现了很大的改进:
// needs Scala2.10M4 scala> import scala.reflect.runtime.{universe => u} import scala.reflect.runtime.{universe=>u} scala> val t = u.typeOf[List[_]] t: reflect.runtime.universe.Type = List[Any] scala> t.declarations res10: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(constructor List, method companion, method isEmpty, method head, method tail, method ::, method :::, method reverse_:::, method mapConserve, method ++, method +:, method toList, method take, method drop, method slice, method takeRight, method splitAt, method takeWhile, method dropWhile, method span, method reverse, method stringPrefix, method toStream, method removeDuplicates)
新的reflection库的文档仍然丢失,但正在进行中。 它允许在REPL中以简单的方式使用scalac:
scala> u reify { List(1,2,3) map (_+1) } res14: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]](immutable.this.List.apply(1, 2, 3).map(((x$1) => x$1.$plus(1)))(immutable.this.List.canBuildFrom)) scala> import scala.tools.reflect.ToolBox import scala.tools.reflect.ToolBox scala> import scala.reflect.runtime.{currentMirror => m} import scala.reflect.runtime.{currentMirror=>m} scala> val tb = m.mkToolBox() tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@32f7fa37 scala> tb.parseExpr("List(1,2,3) map (_+1)") res16: tb.u.Tree = List(1, 2, 3).map(((x$1) => x$1.$plus(1))) scala> tb.runExpr(res16) res18: Any = List(2, 3, 4)
当我们想知道Scala代码是如何在内部进行转换的时候,这是更大的。 以前需要键入scala -Xprint:typer -e "List(1,2,3) map (_+1)"
来获取内部表示。 此外,在新版本中还有一些小的改进,例如:
scala> :type Predef scala.Predef.type
Scaladoc将获得一些types层次结构图 (点击types层次结构)。
有了macros,现在有可能以很好的方式改善错误信息。 有一个名为expecty的库,它是这样做的:
// copied from GitHub page import org.expecty.Expecty case class Person(name: String = "Fred", age: Int = 42) { def say(words: String*) = words.mkString(" ") } val person = Person() val expect = new Expecty() // Passing expectations expect { person.name == "Fred" person.age * 2 == 84 person.say("Hi", "from", "Expecty!") == "Hi from Expecty!" } // Failing expectation val word1 = "ping" val word2 = "pong" expect { person.say(word1, word2) == "pong pong" } /* Output: java.lang.AssertionError: person.say(word1, word2) == "pong pong" | | | | | | | ping pong false | ping pong Person(Fred,42) */
有一个工具可以让你findGitHub上的库,叫做ls.implicit.ly 。
IDE现在有一些语义突出显示,以显示一个成员是否是一个对象/types/方法/不pipe。 ScalaIDE的语义突出特征。
REPL的javap特性只是对本地javap的调用,因此它不是一个非常丰富的工具。 您必须完全限定模块的名称:
scala> :javap scala.collection.immutable.List Compiled from "List.scala" public abstract class scala.collection.immutable.List extends scala.collection.AbstractSeq implements scala.collection.immutable.LinearSeq,scala.Product,scala.collection.LinearSeqOptimized{ ...
前段时间我写了一个关于如何将Scala代码编译成Bytecode的总结 ,它提供了很多需要了解的事情。
而最好的:这一切都是在过去几个月里完成的!
那么,如何在REPL中使用所有这些东西呢? 那么,这是不可能的…还没有。 ;)
但是我可以告诉你,有一天我们会有这样的REPL。 如果我们想看到它,REPL向我们显示了文档。 一个REPL,让我们与它沟通(也许就像lambdabot )。 一个REPL让我们做一些很酷的事情,我们仍然无法想象。 我不知道什么时候会这样,但是我知道在过去的几年中做了很多事情,而且我知道在未来几年会有更多的事情要做。
Javap的作品,但你指向它scala.Predef.List
,这是一个type
,而不是一个class
。 把它指向scala.collection.immutable.List
。
现在,大多数情况下只需input一个值,看看结果的types就足够了。 使用:type
有时可能会有所帮助。 不过,我发现使用getClass
是一个非常糟糕的方法。
此外,你有时混合types和价值观。 例如,在这里你可以参考object ::
scala> `::`.getClass res79: java.lang.Class[_ <: object scala.collection.immutable.::] = class scala.collection.immutable.$colon$colon$
在这里你可以参考class ::
scala> classOf[`::`[Int]] res81: java.lang.Class[::[Int]] = class scala.collection.immutable.$colon$colon
对象和类不是同一个东西,事实上,有一个同名的对象和类的共同模式,并且有一个特定的关系名称:同伴。
而不是dir
,只需使用选项卡完成:
scala> "abc". + asInstanceOf charAt codePointAt codePointBefore codePointCount compareTo compareToIgnoreCase concat contains contentEquals endsWith equalsIgnoreCase getBytes getChars indexOf intern isEmpty isInstanceOf lastIndexOf length matches offsetByCodePoints regionMatches replace replaceAll replaceFirst split startsWith subSequence substring toCharArray toLowerCase toString toUpperCase trim scala> "abc".compareTo compareTo compareToIgnoreCase scala> "abc".compareTo def compareTo(String): Int
如果你进入权力模式,你会得到更多的信息,但是这对初学者来说很难。 以上显示了types,方法和方法签名。 Javap会反编译东西,虽然这需要你有一个很好的字节码处理。
还有其他的东西 – 一定要查找:help
,看看有什么可用的。
文档只能通过scaladoc API使用。 在浏览器中保持打开状态,并使用searchfunction快速查找类和方法。 另外请注意,与Java相反,您不需要浏览inheritance列表以获取方法的描述。
而且他们的search完全符合标准。 我怀疑你没有花太多的时间在scaladoc上,因为其他的doc工具在这方面并不合适。 想到Javadoc – 浏览包和类是非常糟糕的。
如果您有特定的问题堆栈溢出风格,使用符号猎犬search符号。
使用夜间 Scaladocs:它们会与您使用的任何版本不同,但它们始终是最完整的。 此外,现在它们在许多方面都好得多:您可以使用TAB在帧之间切换,在search框上自动对焦,可以使用箭头在滤波后在左边框上导航,然后按ENTER键以使所选元素出现在右边的框架上。 他们有隐式方法的列表,并有类图。
我已经做了一个不太强大的REPL,和一个更差的Scaladoc – 他们一起工作。 当然,我跳到行李箱(现在头),只是为了让我的手完成标签。
请注意, scala 2.11.8 Scala REPL中的新选项卡完成可以促进types的探索/发现。
现在它包括:
-
驼峰完成:
尝试:
(l: List[Int]).rro
TAB ,
它扩展到:
(l: List[Int]).reduceRightOption
-
通过键入名称的任何CamelCased部分查找成员:
尝试:
classOf[String].typ
getAnnotationsByType
TAB ,获取getAnnotationsByType
,getComponentType
等 -
完整的bean getters没有inputget:
尝试:
(d: java.util.Date).day
TAB -
按TAB两次查看方法签名:
尝试:
List(1,2,3).part
TAB ,
完成到:
List(1,2,3).partition
;
再次按TAB键显示:
def partition(p: Int => Boolean): (List[Int], List[Int])
您需要将完全限定的类名传递给javap
。
首先使用classOf
:
scala> classOf[List[_]] res2: java.lang.Class[List[_]] = class scala.collection.immutable.List
然后使用javap
(对于我来说,并不适用于repl:“:javap在这个平台上不可用”)所以例子是从命令行,在repl中,我相信你不需要指定classpath:
d:\bin\scala\scala-2.9.1-1\lib>javap -classpath scala-library.jar "scala.collection.immutable.List"
但我怀疑这会帮助你。 也许你正在尝试使用你在dynamic语言中使用的技术。 我极less在scala中使用repl(在javascript中经常使用它)。 IDE和来源是我的全部。