Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

REPLライフをもっと快適に

10,477 views

Published on

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

Published in: Technology
  • Be the first to comment

REPLライフをもっと快適に

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

×