REPLライフをもっと快適に
-
Upload
sohta -
Category
Technology
-
view
3.415 -
download
0
description
Transcript of REPLライフをもっと快適に
REPLライフをもっと快適に!~Stuart SierraのClojureワークフローの紹介~
Clojure夜会 2014/10/10 @athos0220
REPL!使ってますか?
user=>
評価
入力した式の結果がすぐ確認できる!高速なフィードバックループ
Check
user=>
(ns example)
(defn fib [x] ...)
Edit
評価
Check
user=>
(ns example)
(defn fib [x] ...)
Edit
評価
ロード!or!
リロード
Check
user=>
(ns example)
(defn fib [x] ...)
Edit
評価
修正!or !
次のステップへ
ロード!or!
リロード
Check
user=>
(ns example)
(defn fib [x] ...)
Edit
評価
修正!or !
次のステップへ
ロード!or!
リロード
失敗
Check
user=>
(ns example)
(defn fib [x] ...)
Edit修正・確認のフィードバックループを高速に!
回すにはリロードが重要!
評価
修正!or !
次のステップへ
ロード!or!
リロード
失敗
リロードにまつわる問題• プロトコルやマルチメソッド、マクロ等に対する変更が正しく反映されない
• 名前空間のロード順に制約がある場合に、制約を満たさない順でロードが実行される
• 通信コネクションの切断・接続やスレッドの停止・開始等が正しく実行されない
これらを解決するには!ツールのサポートと設計の工夫が必要
快適なREPLライフへの3ステップ
• tools.namespaceを使おう
• user.cljを作ろう
• コンポーネント単位でシステムを作ろう
tools.namespaceを使おう• 名前空間の依存関係を解析して適切な順番でロードし直す
• リロードによって不整合が起こらないようにしてくれる
• 平たくいえば (require … :reload) の強化版
A!B!
A’!!
C
A’!B!C
(require … :reload)
A!B!
A’!!
C
A’!!
Ctools.namespace
例
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=>
user.cljを作ろう• tools.namespaceはリロード毎に名前空間を作り直すので…
• REPLで定義した変数・関数が消える
• REPLでrequireした名前空間も見えなくなる
• REPLがクラスパス中のuser.cljを自動で読み込むことを利用して、そこに必要なものを定義
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"]}})
(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の例
コンポーネント単位でシステムを作ろう
• Componentライブラリ
• システムを“コンポーネント”に分割
• コンポーネント=状態を共有する関数群
• DBアクセス、外部サービス、atomやref, etc.
• コンポーネントの起動・停止方法を提供し、依存関係を考慮した順序で呼び出す
コンポーネント単位でシステムを作ろう
System
DB
Users!API
Email!Service
Queue!Service
コンポーネント単位でシステムを作ろう
(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メソッド内にまとめる
コンポーネント単位でシステムを作ろう(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が依存関係を考えて各コンポーネントを起動・停止
まとめ
• 動的に変更を取り込むのは動的言語の醍醐味
• 各ステップを単独で導入するだけでも効果あり
• 高速なフィードバックループを実現し、快適なREPLライフを!
参考文献
• My Clojure Workflow, Reloaded
• Clojure in the Large [InfoQ]
• tools.namespace [GitHub]
• Component Library [GitHub]