📝Clojure REPL Driven Development

📝Clojure REPL Driven Development

🏷Clojure 🏷ソフトウェア開発手法

REPL-Oriented Programmingとも. RDDと略されたりもする.

ソースコードを即時にエディタで評価してインライン表示などすることで,素早くフィードバックをえることができる.

正確にはREPLにエディタからソースコードを評価した結果をもらったものを表示だが,操作的にはソースコードと対話するように開発ができる.

TDDは細かくテストを書くことで素早くフィードバックを得ることで先の不安を事前に取り除き前に進むための確信を得ることが目的のひとつだった. 継続的インテグレーション,継続的デリバリーなども. それらのサイクルの単位よりもRDDは圧倒的にフィードバックが早い! もはや最小単位.

REPLとは #

REPLとは以下の略.

  • read
  • evaluate
  • print
  • loop

REPLの説明は以下がわかりやすかった. 少し抜粋.

ref: 🔗REPL Driven Programming - tyano’s Techlog

REPLでnamespaceを読み込むということは簡易実行環境ではなく あなたが書いたプログラム内部に潜り込んでコンソールを開いた状態 そのもの. Clojureにインタープリタは存在しない. 関数をバイトコードにして実行するのみ.


Road to Common Lispの中で Lisp as a System という章も,例題がCommon Lispではあるものの実体はRDD.

  1. Lispプロセスを起動。
  2. プロジェクトを読み込む。
  3. コードをエディタで編集。
  4. 動作中のプロセスに、編集したコードだけをコンパイルさせる。
  5. プロセス中の変更したコードと対話。 REPL や HTTP request などを通して行う。
  6. 出力を調査 (コンソール、ブラウザなどで)。3に戻る。

> Common Lispを書くことは、生物や呼吸する組織とやりとりすること、もしくは 熱心な助手に物事を教えることのように感じることができます。

ref: 💻A Road to Common Lisp - Steve Losh(2018)

EmacsでのClojure REPL Driven Developmentの具体的な方法 #

  • M-x cider-jack-in (REPLにEmacsから接続)
    • M-x cider-load-file (ファイル単位でevaluate)
    • M-x cider-eval-last-sexp (フォーム単位でevaluate & インラインで結果表示)
    • M-x cider-eval-last-sexp-to-repl(フォーム単位でREPLに送信してevaluate)

ref: 🔗Column: REPL 駆動開発を取り入れて Ring でもう少し遊んでみる — Clojure の日本語ガイド

Design Journal #

RDDでコードを書くと,まずごにょごにょとアイデアを文で書いたりそれを小さな関数で実装して評価してみたりを繰り返しながら,つまりエディタと対話しながら小さな部品を組み立てていき,それらを組み合わせて大きな機能をつくる.

そのごにょごにょの部分はソースコードの下のほうにコメントアウトしつつ残しておくことで,どういう設計意図があってこのコードを実装したのかの履歴を,コードとともに残しておこうという考え.

こういうことができるのは,REPLで関数をかんたんに評価できるということもあるが,別の側面としてはClojureが関数型言語なので簡潔に副作用もなく部品としてコードを残しやすいという側面がある(と個人的には思っている).

実際の Design Journal実施例 はこちら.

ref: REPL Driven Development - DEV Community

Design Journal Tisp with Clojure Cider #

comment マクロをつかうとまとめてコメントアウトできる.

式の評価結果をそのまま次の行に出力すると後で思い出すときにいちいち評価しなくてもいい.

  • M-x cider-pprint-eval-defun-to-comment
  • M-x cider-pprint-eval-last-sexp-to-comment

ただしcommentと合わせて使うと評価が動かないのでこれをつかうときはcommentを外す.

Reloaded Workflow #

Stuart Sierra さんの提唱で有名になった?開発手法(2013).

ref: 🔗Clojure Workflow Reloaded

Clojureの起動, 特にJVMの初回立ち上げが重くて気軽に再起動できないからこそ生まれた工夫.

アプリをシングルトンなJVMとして起動するのではなくてインスタンスとして起動することで古いインスタンスは捨ててしまう(i.e.ガーベージコレクションかな?).

とくにresetを連発してライトにシステム再起動, CIDERで M-x cider-ns-refreshの前後にsuspendとresumeをhookさせるとよいとか.

.dir-locals.に記載.

((clojure-mode . ((cider-ns-refresh-before-fn . "integrant.repl/suspend")
                  (cider-ns-refresh-after-fn  . "integrant.repl/resume"))))

💡考察: reloaded workflowはdocker-composeに似ている #

コレはたしかによいね, REPLの再起動が結構ストレスだったので. reset 連発するぞ.

docker-composeに発想は似ているな. dockerごとにサービスを定義してdocker-composeで依存関係を制御しつつ立ち上げるところ. docker-composeの再起動ってどのくらいの速さだっけ?

しかしreplのみで再起動のほうが速そうではある. システムテストならdockerまるごと再起動が必要もしれないが少なくもと開発なら気軽にEmacsから再起動したい.

See also #

clojure.repl #

ClojureのREPLでの便利ツールライブラリ.

clojure.repl - Clojure v1.10.3 API documentation

REPLでrequireでライブラリを一切合切ロードしてつかう.

(require '[clojure.repl :refer :all])
  • doc: 関数のドキュメントを表示.
    • cider-doc/cider-clojuredocs/cider-javadocだとEmacs経由で参照できる.
  • source: 関数のソースを表示.

Clojure RDD Tips #

tips: 便利ツールをいろいろ導入 #

REPLを起動するとuser.cljが自動的に読み込まれる. 例えばpathに dev/srcを設定してその中に user.cljをおいておくと読み込まれる.

そのuser.cljにREPLで利用する便利関数をいろいろ詰め込んでおくと, 自分専用の開発ToolBoxの完成.

howto: namespaceの定義を消したい #

clojure.coreには, ns-unmap がありこれでnamespaceからsymbolを消すことができる.

ciderだと cider-undefをつかうとbetter.

参考リンク #


see also: 🖊REPL 駆動開発について(REPL Driven Development) 調べたメモ | Futurismo

Active Recalls #

REPLとは何の略ですか? #

read, evaluate, print, loop

REPL Driven Developmentとはなんですか? #

REPLを使って対話的に開発を行う手法.

エディタにコードを書いて,REPLに送信して評価して即座にアウトプットのフィードバックを得ることにで,次に何をするかを考えることができる.