ESS+R使いがClojureで遊びたくて作ったeval-in-repl.el (C-RETでREPL評価)の紹介


これは何か?

Emacs Advent Calendar 2014(http://qiita.com/advent-calendar/2014/emacs )の4日目です。

各種lisp、Python、シェルスクリプトなどでESS風の"C-RET"でインタープリタor REPL起動/現在行評価/選択範囲評価をやって、送ったコードと評価結果をインタープリタor REPL側に表示するというelispの紹介です。MELPAにあります(http://melpa.org/#/eval-in-repl )。以下、前置きが長いので、スクリーンショットから見た方がいいかも。。

2014-12-27追記: OCamlとHy(Pythonをlispにしてしまった変態言語)のサポートを追加しました。

2014-12-30追記: Refactoringついでにscreencastを作成しました。
https://github.com/kaz-yos/eval-in-repl
YouTube版
https://www.youtube.com/watch?v=gNBlF67e-0w&feature=youtu.be

経緯とESSについて

私がChurch of Emacsに入信したのは統計解析言語/環境のRを使う環境が欲かったからでした。今はRの環境というとRStudio (http://www.rstudio.com/products/rstudio/ )というIDEが主流になっていますが、当時はRstudioは出たか出ないかぐらいの時期で、まだいまいちだったように思います。EmacsのRのインターフェースとしては、Emacs Speak Statistics (ESS; http://ess.r-project.org )が主流(というか唯一)と思います。私もこれを数年使っております。

統計解析はインタラクティブな作業なので、コードを書いてはインタープリタで評価して結果を見ながら書きすすめる、というワークフローになります。このための便利な機能として、ESSには、"C-RET"(control-return)で全部すませるという機能があります。

コードのバッファーで"C-RET"すると初回はインタープリタの起動をします。以降の"C-RET"は、選択範囲なしだと現在行をインタープリタに送って評価、カーソルは次の行に進みます。要するにC-RETを連打していれば、順々に必要なところを評価していけるわけです。選択範囲のある状態だと選択範囲のみをインタープリタに送りこみます。これは、複数行を送るのにも使えますし、行内の一部だけを評価するのにも使えます。

この間設定にもよりますが、インタープリタ側では送られたコードの下に評価結果の表示、次に送られたコードの下に評価結果の表示、という風に履歴的なものが残るので、ちょっと前に戻って見たりしやすいです。この機能はESS本体には、RStudioの同様の機能から取りこまれたようですが、野良拡張としては以前からあり、便利に使っておりました。

eval-in-repl.elについて

Emacsには偉大な先人たちのおかげで大抵の言語にはメジャーモードが大抵あって、インタープリタやREPLとの連携も付いていることが多いです。"C-x C-r"での選択範囲評価はだいたい付いていると思います。ただ、なぜか文化的なものなのかインタープリタ/REPLを使って評価しているのに、インタープリタ/REPLのバッファには何も表示しないor評価結果だけ表示のものが多いです。あとはミニバッファに一瞬表示されて消えたりとか。。

こういった点が不満だったのと、インタープリタor REPL起動/現在行評価/選択範囲評価をすべて"C-RET"でやりたい!ということで、elisp素人の私がなんとか作成したのがeval-in-repl.el(http://melpa.org/#/eval-in-repl )です。現在、下記の言語に対応しています。モードにはなっていなくて、現存する各言語のメジャーモードに"C-RET"インターフェースを載せるかたちになっています。しくみは簡単で、コードを選択してインタープリタor REPL側に貼って実行、コード側に戻って、次のコードに進むということをやっているだけです。設定についてはgithub repoをご覧ください。

eval-in-repl-ielm.el    for Emacs Lisp    (via ielm)
eval-in-repl-cider.el   for Clojure       (via cider.el)
eval-in-repl-slime.el   for SLIME         (via slime.el)
eval-in-repl-geiser.el  for Racket/Scheme (via geiser.el)
eval-in-repl-racket.el  for Racket        (via racket-mode.el)
eval-in-repl-scheme.el  for Scheme        (via scheme.el and cmuscheme.el)
eval-in-repl-python.el  for Python        (via python.el)
eval-in-repl-shell.el   for Shell         (via essh.el)
eval-in-repl-sml.el     for Standard ML   (via sml-mode.el and ess.el)
eval-in-repl-ruby.el    for Ruby          (via ruby-mode.el, inf-ruby.el, and ess.el)
eval-in-repl-ocaml.el   for OCaml         (via tuareg.el and ess.el)
eval-in-repl-hy.el      for Hy            (via hy-mode.el)

各種lispのものは、選択範囲なしで"C-RET"すると現在カーソルのある行の上で一番近くではじまるトップレベルのS式を実行します(1列目の"("が見つかるまでS式を戻るという簡単な設計ですが)。Pythonはpython.elにブロックを認識する機能があったのでカーソル位置ではじまるブロックを実行します。Shell, SML, Rubyはメジャーモードにブロック選択が見当たらなかったので、現在行or選択範囲実行のみです。

eval-in-repl.elの使いかた (Clojure版を例に)

cider.elを使用するClojure版のスクリーンショットを貼っていきます。Seven Web Frameworks in Seven Weeksという本に乗っていたClojureからSQLiteを操作するデモです。

右のバッファにコードだけ開かれている状態(画像の穴はセキュリティ上)。一番目のexpressionのあとで"C-RET"する。

pic0250.png

Java VMの立ち上げにちょっともたつきます(他の言語は速いです)が、cider.elを介してClojure REPLが左に登場。ミニバッファによると"Design is about pulling things apart. -Rich Hickey"とのことです。Clojureはopinonated languageだそうなのでこれぐらいは当然でしょう。

pic0305.png

REPL起動時にカーソルがREPL側にいってしまうのを、技術不足でまだ直せていないので、コード側に戻って"C-RET"します。最初のトップレベルのS式がREPLに送られて評価されます。nilが評価結果です。

pic0352.png

カーソルは次のS式のあとに飛ぶので、そのまま"C-RET"を連打するとS式を一個づつ評価します。

pic0419.png

projectというシンボルだけ選択して評価しましょう。ああ、まだ定義してなかったのでエラーになりました。定義のS式を先に評価しましょう。(次の画像のREPLの上1/3ぐらいのところにprojectの定義後の評価結果があります。Clojureのmapになっているようです。コードと対になっているので、簡易的なログとして使えます。)

pic0446.png

面倒なら複数のS式を選択して"C-RET"でOKです(IELMを起動するelisp版は残念ながら、一度にS式一個しか受け付けません)。外側をquoteでずさんにコメントアウトしているのは気にしないでください。

pic0512.png

4つのSELECT文が送られて4つの結果が返ってきました

pic0522.png

同じ言語の複数のREPLバッファがある場合の挙動は、メジャーモードがREPL移行のコマンドを定義しているかに依存しているので、ちょっと微妙かもしれません。

他の言語版(Python, Emacs Lisp, SML, shell, 各種lispなど)

Python版のスクリーンショットです。python.elをバックエンドに使用します。でも、Pythonに関してはEmacs IPython Notebook2 (EIN2; https://github.com/millejoh/emacs-ipython-notebook )を使ったほうがいいかも。

python.png

IELMに接続するEmacs Lisp版。

elisp.png

Racket版。geiser.elをバックエンドに使用します。

racket.png

SLIMEを介してSBCL common lispを使用しているもの。

slime.png

SML版。sml-mode.elをバックエンドに使用します。CourseraのProgramming Languages(https://www.coursera.org/course/proglang )で使うために作成。type inferenceは良いものと学びました。tuareg.elをバックエンドにしたOCaml版を検討中。

sml.png

shell版。essh.elをバックエンドに使用します。shell scriptでFibonacciを書いた先人がいるのに驚愕しました。

shell.png

私のつたないelispで書かれているので、いろいろ問題もあるかと思います。アドバイス等いただけると幸いです。