如何调查对象/类型/等.来自 Scala REPL? [英] How to investigate objects/types/etc. from Scala REPL?

查看:45
本文介绍了如何调查对象/类型/等.来自 Scala REPL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用 Scala 有一段时间了,并用它编写了一个 10,000 多行的程序,但我仍然对一些内部工作感到困惑.在已经非常熟悉 Java、C 和 Lisp 之后,我从 Python 来到了 Scala,但即便如此,它仍然进展缓慢,而且一个巨大的问题是我在尝试调查对象/类型的内部工作时经常发现的令人沮丧的困难/类/等.与 Python 相比,使用 Scala REPL.在 Python 中,您可以使用 foo 调查任何对象 foo(类型、全局变量中的对象、内置函数等)以查看事物的计算结果,type(foo) 显示它的类型,dir(foo) 告诉你可以调用的方法,help(foo)获取内置文档.您甚至可以执行诸如 help("re") 之类的操作来查找有关名为 re 的包(其中包含正则表达式对象和方法)的文档,即使有不是与之关联的对象.

I've been working with Scala for a while now and have written a 10,000+ line program with it, but I'm still confused by some of the inner workings. I came to Scala from Python after already having intimate familiarity with Java, C and Lisp, but even so it's been slow going, and a huge problem is the frustrating difficulty I've often found when trying to investigate the inner workings of objects/types/classes/etc. using the Scala REPL as compared with Python. In Python you can investigate any object foo (type, object in a global variable, built-in function, etc.) using foo to see what the thing evaluates to, type(foo) to show its type, dir(foo) to tell you the methods you can call on it, and help(foo) to get the built-in documentation. You can even do things like help("re") to find out the documentation on the package named re (which holds regular-expression objects and methods), even though there isn't an object associated with it.

在 Scala 中,您可以尝试在线阅读文档、查找库的源代码等,但是对于您不知道它们在哪里甚至不知道它们是什么的事情,这通常会非常困难(考虑到大量的类型层次结构,它通常是一个很大的块)——东西在不同的地方(包scalaPredef、各种隐式转换、符号像 :: 这对 Google 来说几乎是不可能的).REPL 应该是直接探索的方式,但实际上,事情要神秘得多.假设我在某处看到了对 foo 的引用,但我不知道它是什么.显然没有使用 REPL 系统地调查 Scala 事物的指南"这样的东西,但以下是我经过大量反复试验后拼凑出来的:

In Scala, you can try and read the documentation online, go look up the source code to the library, etc., but this can often be very difficult for things where you don't know where or even what they are (and it's often a big chunk to bite off, given the voluminous type hierarchy) -- stuff is floating around in various places (package scala, Predef, various implicit conversions, symbols like :: that are nearly impossible to Google). The REPL should be the way to explore directly, but in reality, things are far more mysterious. Say that I've seen a reference to foo somewhere, but I have no idea what it is. There's apparently no such thing as a "guide to systematically investigating Scala thingies with the REPL", but the following is what I've pieced together after a great deal of trial and error:

  1. 如果 foo 是一个值(大概包括存储在变量中的东西加上伴随对象和其他 Scala objects),你可以评估 foo 直接.这应该告诉您结果的类型和值.有时结果会有所帮助,有时则不会.
  2. 如果 foo 是一个值,你可以使用 :type foo 来获取它的类型.(不一定有启发性.)如果在函数调用中使用 this,则无需调用函数即可获得返回值的类型.
  3. 如果 foo 是一个值,你可以使用 foo.getClass 来获取它的类.(通常比前一个更有启发性,但是对象的类与其类型有何不同?)
  4. 对于类 foo,您可以使用 classOf[foo],尽管结果的含义并不明显.
  5. 理论上,您可以使用 :javap foo 来反汇编一个类——这应该是最有用的,但对我来说完全一致地失败了.
  6. 有时您必须将错误消息拼凑起来.
  1. If foo is a value (which presumably includes things stored in variables plus companion objects and other Scala objects), you can evaluate foo directly. This ought to tell you the type and value of the result. Sometimes the result is helpful, sometimes not.
  2. If foo is a value, you can use :type foo to get its type. (Not necessarily enlightening.) If you use this on a function call, you get the type of the return value, without calling the function.
  3. If foo is a value, you can use foo.getClass to get its class. (Often more enlightening than the previous, but how does an object's class differ from its type?)
  4. For a class foo, you can use classOf[foo], although it's not obvious what the result means.
  5. Theoretically, you can use :javap foo to disassemble a class -- which should be the most useful of all, but fails entirely and uniformly for me.
  6. Sometimes you have to piece things together from error messages.

使用: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
              ^

好的,现在让我们尝试一个简单的例子.

OK, now let's try a simple example.

scala> 5
res63: Int = 5

scala> :type 5
Int

scala> 5.getClass
res64: java.lang.Class[Int] = int

足够简单...

现在,让我们尝试一些不那么明显的真实案例:

Now, let's try some real cases, where it's not so obvious:

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 中的方式……但是 Google 上的 Scala 文档告诉我 Predefobject Predef extends LowPriorityImplicits —— 怎么能我从 REPL 推断出这个?我该如何查看其中的内容?

What does this mean? Why is the type of Predef simply type, whereas the class is scala.Predef$? I gather that the $ is the way that companion objects are shoehorned into Java ... but Scala docs on Google tell me that Predef is object Predef extends LowPriorityImplicits -- how can I deduce this from the REPL? And how can I look into what's in it?

好的,让我们尝试另一个令人困惑的事情:

OK, let's try another confusing thing:

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

好吧,这让我非常困惑,最终我不得不去阅读源代码来理解这一切.

OK, this left me hopelessly confused, and eventually I had to go read the source code to make sense of this all.

所以,我的问题是:

  1. 真正的 Scala 专家推荐的使用 REPL 来理解 Scala 对象、类、方法等的最佳方法是什么,或者至少可以通过 REPL 尽最大努力研究它们?
  2. 我如何让 :javap 从 REPL 工作到内置的东西?(默认情况下不应该工作吗?)
  1. What's the recommended best way from the true Scala experts of using the REPL to make sense of Scala objects, classes, methods, etc., or at least investigate them as best as can be done from the REPL?
  2. How do I get :javap working from the REPL for built-in stuff? (Shouldn't it work by default?)

感谢您的启发.

推荐答案

您提到了 Scala 缺乏的一个重要点:文档.

You mentioned an important point which Scala lacks a bit: the documentation.

REPL 是一个很棒的工具,但它并不像它那样出色.有太多缺失的功能和可以改进的功能 - 您的帖子中提到了其中一些.Scaladoc 也是一个不错的工具,但离完美还差得很远.此外,API 中的许多代码尚未记录或记录太少,并且经常缺少代码示例.IDE 完全是 ob 错误,与 Java IDE 向我们展示的可能性相比,它们看起来像一些幼儿园玩具.

The REPL is a fantastic tool, but it is not as fantastic at it can be. There are too much missing features and features which can be improved - some of them are mentioned in your post. Scaladoc is a nice tool, too, but is far away to be perfect. Furthermore lots of code in the API is not yet or too less documented and code examples are often missing. The IDEs are full ob bugs and compared to the possibilities Java IDEs show us they look like some kindergarten toys.

尽管如此,Scalas 当前的工具与我 2-3 年前开始学习 Scala 时可用的工具相比存在巨大差异.当时 IDE 会在后台永久编译一些垃圾,编译器每隔几分钟就会崩溃,并且一些文档完全不存在.我经常受到愤怒攻击,并希望 Scala 作者死亡和腐败.

Nevertheless there is a gigantic difference of Scalas current tools compared to the tools available as I started to learn Scala 2-3 years ago. At that time IDEs compiled permanently some trash in the background, the compiler crashed every few minutes and some documentation was absolutely nonexistent. Frequently I got rage attacks and wished death and corruption to Scala authors.

现在呢?我不再有任何这些愤怒攻击了.因为我们目前拥有的工具虽然不完美,但很棒!

And now? I do not have any of these rage attacks any more. Because the tools we currently have are great although the are not perfect!

docs.scala-lang.org,其中总结了很多很棒的文档.有教程、备忘单、词汇表、指南和更多很棒的东西.另一个很棒的工具是 Scalex,它甚至可以找到人们能想到的最奇怪的运算符.它是 Scalas Hoogle,尽管还不如他的伟大理想,但它非常有用.

There is docs.scala-lang.org, which summarizes a lot of great documentation. There are Tutorials, Cheat-sheets, Glossaries, Guides and a lot of more great stuff. Another great tools is Scalex, which can find even the weirdest operator one can think of. It is Scalas Hoogle and even though it is not yet as good as his great ideal, it is very useful.

Scala2.10 以 Scalas 自己的反射库的形式进行了重大改进:

Great improvements are coming with Scala2.10 in form of Scalas own Reflection library:

// 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:

Documentation for the new Reflection library is still missing, but in progress. It allows one to use scalac in an easy way inside of the REPL:

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 代码是如何在内部翻译时,这就更重要了.以前 wen 需要输入 scala -Xprint:typer -e "List(1,2,3) map (_+1)" 获取内部表示.此外,新版本还发现了一些小的改进,例如:

This is even greater when we want to know how Scala code is translated internally. Formerly wen need to type scala -Xprint:typer -e "List(1,2,3) map (_+1)" to get the internally representation. Furthermore some small improvements found there way to the new release, for example:

scala> :type Predef
scala.Predef.type

Scaladoc 将获得一些类型层次图(点击类型层次).

Scaladoc will gain some type-hierarchy graph (click on type-hierarchy).

现在可以使用宏以极大的方式改进错误消息.有一个名为 expecty 的库,它执行此操作:

With Macros it is possible now, to improve error messages in a great way. There is a library called expecty, which does this:

// 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)
*/

有一种工具可以让人们找到托管在 GitHub 上的库,名为 ls.implicit.ly.

There is a tool which allows one to find libraries hosted on GitHub, called ls.implicit.ly.

IDE 现在有一些语义突出显示,以显示成员是否是对象/类型/方法/任何东西.ScalaIDE 的语义高亮功能.

The IDEs now have some semantic highlighting, to show if a member is a object/type/method/whatever. The semantic highlighting feature of ScalaIDE.

REPL 的 javap 特性只是对原生 javap 的调用,因此它不是一个非常丰富的工具.您必须完全限定模块的名称:

The javap feature of the REPL is only a call to the native javap, therefore it is not a very featue-rich tool. You have to fully qualify the name of a module:

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如何代码被编译成字节码,它提供了很多需要知道的东西.

Some time ago I have written a summary of how Scala code is compiled to Bytecode, which offers a lot of things to know.

最好的:这一切都是在过去几个月完成的!

And the best: This is all done in the last few months!

那么,如何在 REPL 中使用所有这些东西?嗯,这是不可能的......还没有.;)

So, how to use all of these things inside of the REPL? Well, it is not possible ... not yet. ;)

但我可以告诉你,总有一天我们会有这样的 REPL.一个 REPL,如果我们想查看它,它会向我们展示文档.一个让我们与之通信的 REPL(可能像 lambdabot).REPL 让我们做一些我们仍然无法想象的很酷的事情.我不知道什么时候会变成这样,但我知道过去几年做了很多事情,我知道未来几年会做更多的事情.

But I can tell you that one day we will have such a REPL. A REPL which shows us documentation if we want to see it. A REPL which let us communicate with it (maybe like lambdabot). A REPL which let us do cool things we still cannot imagine. I don't know when this will be the case, but I know that a lot of stuff was done in the last years and I know even greater stuff will be done in the next years.

这篇关于如何调查对象/类型/等.来自 Scala REPL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆