我已经使用Scala工作了一段时间,并编写了超过10,000行的程序,但我仍然对一些内部工作方式感到困惑。我从Python转入Scala,之前已经非常熟悉Java、C和Lisp,但即使这样,进展也很缓慢,一个巨大的问题是在尝试使用Scala REPL调查对象/类型/类等内部机制时,我经常发现非常困难,而这与Python相比就很棘手。在Python中,您可以使用“foo”来研究任何对象(类型、全局变量中的对象、内置函数等等),以查看该对象的求值结果,“type(foo)”显示其类型,“dir(foo)”告诉您可以调用的方法,“help(foo)”获取内置文档。您甚至可以做一些诸如“help('re')”之类的事情,以找出名为“re”的软件包(其中包含正则表达式对象和方法)的文档,即使没有与之关联的对象。
在Scala中,您可以尝试在线阅读文档,查找库的源代码等等,但是对于您不知道它们在哪里或甚至是什么的东西来说,这通常会非常困难(而且通常需要处理大量的类型层次结构)——各种东西都漂浮在不同的地方(包“scala”、“Predef”、各种隐式转换、几乎无法搜索的符号“::”)。REPL应该是直接探索的方式,但实际上,情况要复杂得多。假设我在某个地方看到了对“foo”的引用,但我不知道它是什么。显然,不存在“使用REPL系统地调查Scala事物的指南”,但以下是我在大量试错后拼凑出来的:
- 如果“foo”是一个值(这可能包括存储在变量中的内容以及伴生对象和其他Scala“object”),则可以直接评估“foo”。这应该告诉您结果的类型和值。有时结果有帮助,有时没有。
- 如果“foo”是一个值,则可以使用“:type foo”获取其类型。(并不一定启发人心。)如果在函数调用上使用此选项,则会获取返回值的类型,而不调用该函数。
- 如果“foo”是一个值,则可以使用“foo.getClass”获取其类。(通常比前一个更有启发性,但对象的类与其类型有何不同?)
- 对于类“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
的类型仅为type
,而类为scala.Predef$
?我了解到 $ 是将伴生对象嵌入 Java 的方式... 但是谷歌上的 Scala 文档告诉我 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对象、类、方法等的最佳方式是什么,或者至少从REPL中尽可能地调查它们?
- 如何在REPL中为内置的内容使
:javap
工作? (默认情况下不应该工作吗?)
感谢您提供任何启示。
map
或foldLeft
)- 它们没有提供如何使用它们的示例。对于缺乏高阶函数经验的初学者来说,这是至关重要的。这就是为什么我写“有点缺乏”而不是“完全缺乏”的原因。 - kiritsuku