S-JIS[2011-10-08] 変更履歴

REPL パワーユーザーモード

ScalaREPLのパワーユーザーモードのメモ。


概要

ScalaREPLでは、:power」コマンドを実行するとパワーユーザーモード(個人的にはパワーモードと呼んでいる)になる。

Scala2.9.1の例:

scala> :power
** Power User mode enabled - BEEP BOOP SPIZ **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._ and definitions._ also imported **
** Try :help, vals.<tab>, power.<tab> **

パワーモードでしか扱えないコマンドが増える他に、パワーモードでしか使えない変数が現れる。
この変数はREPLインタープリターが使っているインスタンスそのものだったりするので、色々なことが出来る。

パワーモードで使えるコマンド・変数
コマンド・
変数(型)
説明 備考
:dump REPLインタープリター内部の情報を表示する。  
:wrap REPLの式をラップして、独自処理を実行できる。 →xuweiさんのScala2.9.0のREPLで、すごく便利な裏技を発見した件
:phase    
vals ReplVals REPLインタープリターで使用できる変数を(たぶん)全て保持している。 例えばintpvals.intprepl.intpは同じもの。
repl ILoop REPLを表すオブジェクト。
コロン「:」で始まるコマンドはこのクラスで処理する。
REPLに入力されたコマンドの実行は、以下のような順のメソッド呼び出しで実行される。
  1. ILoop#command(line)
  2. ILoop#interpretStartingWith(code)
  3. IMain#interpret(code)
intp IMain インタープリターを表すオブジェクト。
history History REPLに入力したコマンドの履歴を保持するオブジェクト。  
global Global コンパイルして出来る木構造を扱う為のオブジェクト。 Scalaコンパイラプラグイン

repl(scala.tools.nsc.interpreter.ILoop)

repl.commandの実行

REPL上のコマンドラインから入力されたコマンドは、ILoop#command()に渡されて解釈・実行される。
つまり、自分でrepl.command()を呼び出すと、REPLで実行したかのような結果が得られる。

scala> repl.command("val zzz = 123")
zzz: Int = 123
res16: $r.repl.Result = Result(true,Some(val zzz = 123))

scala> zzz
res17: Int = 123

「REPL上で独自のクラスを生成する」為の関数を作ったりすることも出来る。

scala> def defClass(name: String): Unit = {
     |   val s = """class %s { override def toString() = "<%s>" }""".format(name, name.toLowerCase)
     |   repl.command(s)
     | }
defClass: (name: String)Unit

scala> defClass("Zzz")
defined class Zzz

scala> new Zzz
res22: Zzz = <zzz>

repl.intpの置換

repl.command()がコマンド実行の入り口なので、これをオーバーライドしてやれば独自のREPLコマンドが定義できそう!
なのだが、repl(ILoop)はMainGenericRunner#process()の中で「new ILoop」の形で生成しているので、独自クラスに差し替える隙が無い(嘆)

repl.command()内からさらに呼ばれるintp.interpret()というメソッドがあり、repl.intpはvarなので置き換えられる。

repl.intp = new repl.ILoopInterpreter {
  override def interpret(code: String) = {
    println("code!" + code)
    super.interpret(code)
  }
}
scala> 123
code!123
res0: Int = 123

ただし、それまで保持されていた変数は受け渡されない。(resも0からに戻るし、valsやreplといった変数も使えなくなる)
変数以外にも重要なものを保持しているかもしれないので、実際に使うには不安…。


intp(scala.tools.nsc.interpreter.IMain)

intp.virtualDirectory

名前の通り、仮想ディレクトリー。
REPL上で定義した変数や関数は、内部でクラスを作ってコンパイルされ、仮想的なファイルシステム内にclassファイルのような形で保持している。
(と言うほど分かりやすいクラス名じゃないので使い方が分からないのだが(爆))

仮想ディレクトリー内のファイルを実際のファイルシステムへコピー(保存)する例


intp.classLoader

REPL上で使うクラスをロードする為のクラスローダー。
REPL起動時の-cpオプションや:cp」コマンドによって指定されたクラスパスを含んでいる。


REPLへ戻る / Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま