REPLライフをもっと快適に

21
REPLライフをもっと快適に Stuart SierraClojureワークフローの紹介~ Clojure夜会 2014/10/10 @athos0220

description

Clojure夜会で発表したスライドです。Stuart Sierraが提案しているClojureワークフローについて紹介しています。

Transcript of REPLライフをもっと快適に

Page 1: REPLライフをもっと快適に

REPLライフをもっと快適に!~Stuart SierraのClojureワークフローの紹介~

Clojure夜会 2014/10/10 @athos0220

Page 2: REPLライフをもっと快適に

REPL!使ってますか?

Page 3: REPLライフをもっと快適に

user=>

評価

入力した式の結果がすぐ確認できる!高速なフィードバックループ

Page 4: REPLライフをもっと快適に

Check

user=>

(ns example)

(defn fib [x] ...)

Edit

評価

Page 5: REPLライフをもっと快適に

Check

user=>

(ns example)

(defn fib [x] ...)

Edit

評価

ロード!or!

リロード

Page 6: REPLライフをもっと快適に

Check

user=>

(ns example)

(defn fib [x] ...)

Edit

評価

修正!or !

次のステップへ

ロード!or!

リロード

Page 7: REPLライフをもっと快適に

Check

user=>

(ns example)

(defn fib [x] ...)

Edit

評価

修正!or !

次のステップへ

ロード!or!

リロード

失敗

Page 8: REPLライフをもっと快適に

Check

user=>

(ns example)

(defn fib [x] ...)

Edit修正・確認のフィードバックループを高速に!

回すにはリロードが重要!

評価

修正!or !

次のステップへ

ロード!or!

リロード

失敗

Page 9: REPLライフをもっと快適に

リロードにまつわる問題• プロトコルやマルチメソッド、マクロ等に対する変更が正しく反映されない

• 名前空間のロード順に制約がある場合に、制約を満たさない順でロードが実行される

• 通信コネクションの切断・接続やスレッドの停止・開始等が正しく実行されない

これらを解決するには!ツールのサポートと設計の工夫が必要

Page 10: REPLライフをもっと快適に

快適なREPLライフへの3ステップ

• tools.namespaceを使おう

• user.cljを作ろう

• コンポーネント単位でシステムを作ろう

Page 11: REPLライフをもっと快適に

tools.namespaceを使おう• 名前空間の依存関係を解析して適切な順番でロードし直す

• リロードによって不整合が起こらないようにしてくれる

• 平たくいえば (require … :reload) の強化版

A!B!

A’!!

C

A’!B!C

(require … :reload)

A!B!

A’!!

C

A’!!

Ctools.namespace

Page 12: REPLライフをもっと快適に

tools.namespaceを使おう• 依存ライブラリとして以下を追加(2014/10現在)

!

• (require … :reload) の代わりに (refresh) を使う

!

!

[org.clojure/tools.namespace “0.2.7”]

user=> (require ‘[clojure.tools.namespace.repl :refer [refresh]])niluser=> (refresh):reloading (reacta.script reacta.scripts.hello):okuser=>

Page 13: REPLライフをもっと快適に

user.cljを作ろう• tools.namespaceはリロード毎に名前空間を作り直すので…

• REPLで定義した変数・関数が消える

• REPLでrequireした名前空間も見えなくなる

• REPLがクラスパス中のuser.cljを自動で読み込むことを利用して、そこに必要なものを定義

Page 14: REPLライフをもっと快適に

user.cljを作ろう• (準備) :devプロファイルでのみ有効になるパスを作る

• リリース版JARファイルにuser.cljが含まれないように

(defproject foobar "0.1.0-SNAPSHOT" :description "TODO" :url "TODO" :license {:name "TODO: Choose a license" :url "http://choosealicense.com/"} :dependencies [[org.clojure/clojure "1.6.0"]] :profiles {:dev {:dependencies [[org.clojure/tools.namespace "0.2.7"]] :source-paths ["dev"]}})

Page 15: REPLライフをもっと快適に

(ns user (:require [clojure.tools.namespace.repl :refer [refresh]] [clojure.repl :refer :all] [clojure.pprint :refer [pp pprint]] [foobar.core :refer :all]))!(def system nil)!(defn init [] (alter-var-root #’system (システム初期化)))!(defn start [] (alter-var-root #'system (システムスタート)))!(defn stop [] (alter-var-root #'system (システムストップ)))!(defn go [] (init) (start))!(defn reset [] (stop) (refresh :after 'user/go))

dev/user.cljの例

Page 16: REPLライフをもっと快適に

コンポーネント単位でシステムを作ろう

• Componentライブラリ

• システムを“コンポーネント”に分割

• コンポーネント=状態を共有する関数群

• DBアクセス、外部サービス、atomやref, etc.

• コンポーネントの起動・停止方法を提供し、依存関係を考慮した順序で呼び出す

Page 17: REPLライフをもっと快適に

コンポーネント単位でシステムを作ろう

System

DB

Users!API

Email!Service

Queue!Service

Page 18: REPLライフをもっと快適に

コンポーネント単位でシステムを作ろう

(ns example.db (:require [com.stuartsierra.component :as comp] …))!(defrecord Database [host port conn] comp/Lifecycle (start [component] (assoc component :conn (db/connect host port))) (stop [component] (.close conn) component))

コンポーネントに関する副作用は!start/stopメソッド内にまとめる

Page 19: REPLライフをもっと快適に

コンポーネント単位でシステムを作ろう(ns example.core (:require [com.stuartsierra.component :as comp] …))!(defn new-system [config] (let [{:keys [host port]} (:db config)] (comp/system-map :database (database host port) :queue-service (queue-service …) :email-service (email-service …) :users-api (comp/using (users-api) [:database :email-service]))))

コンポーネント間の依存関係をデータで表現!⇒Componentが依存関係を考えて各コンポーネントを起動・停止

Page 20: REPLライフをもっと快適に

まとめ

• 動的に変更を取り込むのは動的言語の醍醐味

• 各ステップを単独で導入するだけでも効果あり

• 高速なフィードバックループを実現し、快適なREPLライフを!

Page 21: REPLライフをもっと快適に

参考文献

• My Clojure Workflow, Reloaded

• Clojure in the Large [InfoQ]

• tools.namespace [GitHub]

• Component Library [GitHub]