Scala2.9.0のREPLで、すごく便利な裏技を発見した件(全自動ぁばばばばばばばばばばばばばばば)

以前powerモードの一部の機能紹介しましたが、なんだか2.9.0でREPLのpowerモードの機能がさらに増えていて、しかもかなり便利なものを見つけたので紹介
まず

:power 

と打つとpowerモードという、コンパイラ内部にアクセスして、あんなことやこんなことができる変態的なモードになります。2.8.1だと以下のような感じだったのですが

scala> :power
** Power User mode enabled - BEEP BOOP      **
** scala.tools.nsc._ has been imported      **
** New vals! Try repl, global, power        **
** New cmds! :help to discover them         **
** New defs! Type power.<tab> to reveal     **

scala> :help
All commands can be abbreviated - for example :he instead of :help.

:cp <arg>          add an entry (jar or directory) to the classpath
:help              print this help message
:history [arg]     show the history (optional arg: lines to show)
:h? <line>         search the history
:load <arg>        load and interpret a Scala file
:power             enable power user mode
:quit              exit the interpreter
:replay            reset execution and replay all previous commands
:sh <line>         fork a shell and run a command
:silent            disable/enable automatic printing of results
:completions <arg> generate list of completions for a given String
:dump              displays a view of the interpreter's internal state

2.9.0だと以下のように微妙に違います

scala> :power
** Power User mode enabled - BEEP BOOP WHIR **
** scala.tools.nsc._ has been imported      **
** global._ and definitions._ also imported **
** New vals! Try repl, intp, global, power  **
** New cmds! :help to discover them         **
** New defs! Type power.<tab> to reveal     **

scala> :help
All commands can be abbreviated, e.g. :he instead of :help.
Those marked with a * have more detailed help, e.g. :help imports.

:cp <path>                 add a jar or directory to the classpath
:help [command]            print this summary or command-specific help
:history [num]             show the history (optional num is commands to show)
:h? <string>               search the history
:imports [name name ...]   show import history, identifying sources of names
:implicits [-v]            show the implicits in scope
:javap <path|class>        disassemble a file or class name
:keybindings               show how ctrl-[A-Z] and other keys are bound
:load <path>               load and interpret a Scala file
:paste                     enter paste mode: all input up to ctrl-D compiled together
:power                     enable power user mode
:quit                      exit the interpreter
:replay                    reset execution and replay all previous commands
:sh <command line>         run a shell command (result is implicitly => List[String])
:silent                    disable/enable automatic printing of results
:type <expr>               display the type of an expression without evaluating it
:dump                      displays a view of the interpreter's internal state
:phase <phase>             set the implicit phase for power commands
:wrap <method>           * name of method to wrap around each repl line

で今回紹介するのが、wrapっていうやつ。
wrapって増えてる、なにこれ(´・ω・`)?って偶然見つけたんですが。ScalaのREPLは

:help コマンド名

と打つと以下のように、コマンド自体のhelpが表示されます。で、wrapの説明↓

scala> :help wrap

:wrap
:wrap clear
:wrap <method>

Installs a wrapper around each line entered into the repl.
Currently it must be the simple name of an existing method
with the specific signature shown in the following example.

def timed[T](body: => T): T = {
  val start = System.nanoTime
  try body
  finally println((System.nanoTime - start) + " nanos elapsed.")
}
:wrap timed

If given no argument, :wrap names the wrapper installed.
An argument of clear will remove the wrapper if any is active.
Note that wrappers do not compose (a new one replaces the old
one) and also that the :phase command uses the same machinery,
so setting :wrap will clear any :phase setting.

簡単にいうと、以下のように

def hoge[A](body: => A):A = { /* メソッド本体 */}  
  • 型引数を1つとる
  • 引数は名前渡しで、型引数の型のものを1つ
  • 戻り値の型も引数の型と同じ

というシグネチャのメソッドを定義して、登録することによって、REPLの式を評価するたび毎回その関数にwrapされて実行されるということができるようです!

上記のヘルプの例は、毎回 nano 秒単位で実行時間を計る関数の例ですね。登録するのはこんな感じ。

scala> def timed[T](body: => T): T = { //まず関数自体定義
     |   val start = System.nanoTime
     |   try body
     |   finally println((System.nanoTime - start) + " nanos elapsed.")
     | }
timed: [T](body: => T)T

そして、関数を登録

scala> :wrap timed
Set wrapper to 'timed'

普通の式を実行

scala> 1 to 10 foreach println
1
2
3
4
5
6
7
8
9
10
1477666 nanos elapsed.

実行時間が表示されてる!!!
解除もできる↓

scala> :wrap clear
Cleared execution wrapper.

表示されなくなる↓

scala> Long.MinValue.toHexString.map{_+12312 toChar}.mkString
res2: String = ぐえええええええええええええええ

この時間計るやつとか登録しておけばかなり便利じゃね?

関数の内容はなんでもいいのでこんなことも↓

scala>  def ababa[A](body: => A):A ={
   val r = body
   println(Long.MaxValue.toHexString.map{_ + 12298 toChar}.mkString)
   r
 }
ababa: [A](body: => A)A

scala> :wrap ababa
Set wrapper to 'ababa'

scala> 1 to 10 sum
ぁばばばばばばばばばばばばばばば
res3: Int = 55

毎回全自動で「ぁばばばばばばばばばばばばばばば」が表示される、超うざいオリジナルREPLが( ´∀`*)

他にも、「wrapの関数で、REPLの評価結果を自動で全部ファイルに保存」とか色々工夫次第で夢がひろがりんぐ(´Д`*)

みんなもっと、powerモードごにょごにょして、裏技発見しようぜ(`・ω・´)