web-dev-qa-db-ja.com

オブジェクト/タイプなどを調査する方法。 from Scala REPL?

私はしばらくの間Scalaを使って作業していて、それを使って10,000行以上のプログラムを作成しましたが、それでも内部の仕組みのいくつかに混乱しています。すでにJava、C、LISPに精通した後、ScalaからPythonに来ましたが、それでもゆっくりと進んでおり、大きな問題は私がよく経験する苛立たしい困難です。オブジェクト/タイプ/クラスなどの内部動作を調査しようとしたときに見つかりました。 Pythonと比較してScala REPLを使用します。 Pythonでは、fooを使用して、任意のオブジェクトfoo(型、グローバル変数内のオブジェクト、組み込み関数など)を調査し、物事が何に評価されるかを確認できます。 、type(foo)はその型を示し、dir(foo)は呼び出すことができるメソッドを示し、help(foo)は組み込みのドキュメントを取得します。 help("re")のようなことを実行して、re(正規表現オブジェクトとメソッドを保持する)という名前のパッケージのドキュメントを見つけることもできます。これには、オブジェクトが関連付けられていません。

Scalaでは、オンラインでドキュメントを読んだり、ライブラリのソースコードを調べたりすることができますが、どこにあるのか、何であるのかさえわからない場合は、これは非常に難しいことがよくあります(多くの場合、大量の型階層を考えると、噛み砕く大きな塊)-ものはさまざまな場所に浮かんでいます(パッケージscalaPredef、さまざまな暗黙の変換、::のような記号Googleにとってはほぼ不可能です)。 REPLは直接探索する方法であるはずですが、実際には、物事ははるかに神秘的です。どこかでfooへの参照を見たが、それが何であるかわからないとします。 「REPLでScalaを体系的に調査するためのガイド」のようなものはないようですが、試行錯誤の末、私がまとめたものは次のとおりです。

  1. fooが値(おそらく変数に格納されているものとコンパニオンオブジェクトおよびその他のScala objectsを含む)の場合、fooを直接評価できます。これにより、結果のタイプと値がわかります。結果が役立つ場合もあれば、役に立たない場合もあります。
  2. fooが値の場合、:type fooを使用してそのタイプを取得できます。 (必ずしも啓蒙的ではありません。)これを関数呼び出しで使用すると、関数を呼び出さなくても戻り値の型を取得できます。
  3. fooが値の場合、foo.getClassを使用してそのクラスを取得できます。 (多くの場合、以前よりも啓発的ですが、オブジェクトのクラスはそのタイプとどのように異なりますか?)
  4. クラスfooの場合、classOf[foo]を使用できますが、結果の意味は明らかではありません。
  5. 理論的には、:javap fooを使用してクラスを逆アセンブルできます。これはすべての中で最も便利なはずですが、私にとっては完全かつ均一に失敗します。
  6. エラーメッセージから物事をつなぎ合わせる必要がある場合があります。

: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 ...に靴べらにする方法だと思いますが、GoogleのScalaのドキュメントによると、Predefobject Predef extends LowPriorityImplicitsです。 --REPLからこれをどのように推測できますか?そして、どうすればその中身を調べることができますか?

OK、別の紛らわしいことを試してみましょう:

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、これは私を絶望的に混乱させました、そして結局私はこれをすべて理解するためにソースコードを読まなければなりませんでした。

だから、私の質問は次のとおりです。

  1. Scalaオブジェクト、クラス、メソッドなどを理解するために、または少なくとも調査するためにREPLを使用する真のScala専門家から推奨される最善の方法は何ですか。それらはREPLからできる限り最善ですか?
  2. 組み込みのもののためにREPLから:javapを機能させるにはどうすればよいですか? (デフォルトでは機能しませんか?)

啓蒙に感謝します。

41
Urban Vagabond

Scala少し欠けている重要なポイント:ドキュメント。

REPLは素晴らしいツールですが、それほど素晴らしいツールではありません。不足している機能や改善できる機能が多すぎます。そのうちのいくつかは投稿に記載されています。Scaladocも素晴らしいツールですが、完璧にはほど遠いです。さらに、APIの多くのコードはまだ文書化されていないか、あまり文書化されておらず、コード例が欠落していることがよくあります。IDEは完全なバグであり、可能性と比較されますJava IDEは、幼稚園のおもちゃのように見えることを示しています。

それにもかかわらず、私が学び始めたときに利用可能なツールと比較して、Scalasの現在のツールには大きな違いがありますScala 2-3年前。当時、IDEはバックグラウンドでいくつかのゴミを永久にコンパイルしました。コンパイラ数分ごとにクラッシュし、一部のドキュメントはまったく存在しませんでした。頻繁に怒りの攻撃を受け、Scalaの作者に死と堕落を望みました。

そして今?私はこれらの怒りの攻撃をもう持っていません。私たちが現在持っているツールは完璧ではありませんが素晴らしいからです!

docs.scala-lang.org があり、これは多くの優れたドキュメントを要約しています。チュートリアル、チートシート、用語集、ガイド、その他たくさんのすばらしいものがあります。もう1つの優れたツールは Scalex です。これは、考えられる最も奇妙な演算子でさえ見つけることができます。それはScalas Hoogle であり、彼の偉大な理想ほど良くはありませんが、非常に便利です。

Scalas独自のReflectionライブラリの形でScala2.10が大幅に改善されています。

_// 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はいくつかの type-hierarchyグラフ (type-hierarchyをクリック)を取得します。

マクロを使用すると、エラーメッセージを大幅に改善できるようになりました。 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)
*/
_

ls.implicit.ly と呼ばれるGitHubでホストされているライブラリを見つけることができるツールがあります。

IDEには、メンバーがオブジェクト/タイプ/メソッド/その他であるかどうかを示すために、いくつかのセマンティックハイライトがあります。 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ができると言えます。 A REPL見たい場合はドキュメントを表示します。AREPLこれで通信できます(多分 lambdabot のように) )。AREPLこれにより、まだ想像できないクールなことができます。いつになるかはわかりませんが、ここ数年で多くのことが行われたことはわかっています。そして私は、今後さらに多くのことが行われることを知っています。

33
kiritsuku

Javapは機能しますが、scala.Predef.Listをポイントしています。これはtypeではなくclassです。代わりにscala.collection.immutable.Listをポイントしてください。

これで、ほとんどの場合、値を入力して結果のタイプを確認するだけで十分です。 :typeを使用すると役立つ場合があります。ただし、getClassを使用するのは本当に悪い方法だと思います。

また、タイプと値を混在させている場合もあります。たとえば、ここではオブジェクト::を参照します。

scala> `::`.getClass res79: Java.lang.Class[_ <: object
scala.collection.immutable.::] = class
scala.collection.immutable.$colon$colon$

そしてここであなたはクラス::を参照します:

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

パワーモードに入ると、より多くの情報が得られますが、それは初心者にはほとんどありません。上記は、タイプ、メソッド、およびメソッドシグネチャを示しています。 Javapは逆コンパイルしますが、バイトコードを適切に処理する必要があります。

そこには他のものがあります-必ず:helpを調べて、何が利用できるかを確認してください。

ドキュメントは、scaladocAPIを介してのみ利用できます。ブラウザで開いたままにし、search機能を使用して、クラスとメソッドをすばやく見つけます。また、Javaとは対照的に、メソッドの説明を取得するために継承リストをナビゲートする必要がないことに注意してください。

そして、彼らはシンボルを完全にうまく検索します。そこにある他のドキュメントツールがそれに対応していないので、あなたはscaladocに多くの時間を費やしていないのではないかと思います。 Javadocが思い浮かびます-パッケージやクラスを閲覧するのはひどいです。

Stack Overflowスタイルについて具体的な質問がある場合は、 Symbol Hound を使用してシンボルで検索してください。

nightly Scaladocsを使用します。使用しているバージョンとは異なりますが、常に最も完全なものになります。さらに、現時点では多くの点ではるかに優れています。Tabキーを使用してフレームを切り替えることができ、検索ボックスにオートフォーカスを設定し、矢印を使用してフィルタリング後に左側のフレームをナビゲートし、Enterキーを使用して選択した要素を表示できます。右のフレームに表示されます。それらには暗黙的なメソッドのリストがあり、クラス図があります。

私ははるかに強力ではないREPLとはるかに貧弱なScaladocでやり遂げました-それらは一緒に機能します。確かに、タブ補完を手に入れるためだけにトランク(現在はHEAD)にスキップしました。

5

scala 2.11.8 New tab-Scala REPL の補完により、型の探索/発見が容易になることに注意してください。

現在、次のものが含まれています。

  • キャメルケースの完成:
    試してみてください:
    _(l: List[Int]).rro_TAB
    次のように展開されます:
    _(l: List[Int]).reduceRightOption_

  • 名前のCamelCased部分を入力して、メンバーを検索します。
    試してみてください:
    _classOf[String].typ_TABgetAnnotationsByTypegetComponentTypeなどを取得するには

  • Getと入力せずに完全なBeanゲッター:
    試してみてください:
    _(d: Java.util.Date).day_TAB

  • 押す TAB メソッドシグネチャを確認するために2回:
    試してみてください:
    List(1,2,3).partTAB
    これは次のように完了します。
    List(1,2,3).partition;
    押す TAB 再度表示するには:
    def partition(p: Int => Boolean): (List[Int], List[Int])

4
VonC

完全修飾クラス名をjavapに渡す必要があります。

まず、classOfを使用して取得します。

scala> classOf[List[_]]
res2: Java.lang.Class[List[_]] = class scala.collection.immutable.List

次に、javapを使用します(replからは機能しません: ":javapはこのプラットフォームでは使用できません。")ので、例はコマンドラインからのものです。replでは、クラスパスを指定する必要はないと思います。 :

d:\bin\scala\scala-2.9.1-1\lib>javap -classpath scala-library.jar "scala.collection.immutable.List"

しかし、これがあなたを助けるとは思えません。おそらく、動的言語で使用していた手法を使用しようとしています。 scalaでreplを使用することはめったにありません(javascriptで頻繁に使用します)。IDEとソースがすべてです。

2
Yaroslav