3月からClojureを仕事で書いていまして、その中で開発中によく使っているコマンドや、気に入った関数について振り返りながらまとめます。
※書く内容は4月初めに決めたのですが、投稿が遅くなってしまいました。
これからClojureを書き始める、書き始めようかなという人の参考になればいいなと思います。
Emacsを使った開発のために
Emacsのインストール
最初はEmacs For Mac OS Xを使って開発をしていたのですが、REPLと繋いでいる場合に落ちることがままありました。
そこでEmacs Mac Portを教えていただいて、乗り換えたところほぼ落ちることはなくなりました。快適です。
MacでEmacsを使う方はこちらをおすすめします。
Emacsの設定
基本的には@ayato_p さんが書いた、新: Emacs を使うモダンな Clojure 開発環境を参考にすればイケてる開発環境になると思います。
cider-mode.el
ciderのバージョンは0.12.0です。
-
cider-jack-in (C-c M-j)
REPLに接続します。日々ここから作業スタートです。 -
cider-eval-defun-at-point (C-c C-c)
現在カーソルのあるフォームを評価します。
ある程度コードを書いたらほぼ無意識で使ってます。 -
cider-load-buffer (C-c C-k)
名前空間(ファイル全体)を評価します。 -
cider-switch-to-repl-buffer (C-c C-z)
コードとREPLを行き来します。僕は固定した場所にREPLを出しておくのではなく、これを使ってREPLと行き来するのが好きです。ほんとに便利。 -
cider-find-var (M-.)
カーソル上のVarにジャンプしてくれます。タグジャンプですね。ただ、ライブラリやClojure.coreの関数にも飛んでいけるので非常に便利です。 -
cider-pop-back (M-,)
IDEとかによくある"戻る"です。上のcider-find-varと組み合わせて使うことが多いです。 -
cider-eval-defun-at-point (C-u C-M-x)
フォームにブレークポイントを貼ります。実行されるとデバッグモードに入り、n
でステップ実行します。
とても便利なのですが、たまにうまくデバッグモードに入らないときがあるのが困ったところ。 -
cider-repl-clear-output (C-u C-c C-o)
REPLをクリアします。ターミナルのCtrl-l
と一緒ですね。 -
cider-pretty-print (C-u C-c C-p)
評価した結果をデータ構造が見やすいように表示してくれます。戻り値が意図したとおりのデータ構造になっているか確認するのに便利です。 -
cider-undef (C-c C-u)
評価したフォームをundefします。色々書いてておかしいシンボルかぶったかな?と思った時に。
正直使用頻度は低いです。 -
cider-doc (C-c C-d C-d)
ドキュメントを参照します。僕はこれよりもClojureDocsで見ることのほうが多いのであまり使ってません。 -
cider-javadoc (C-c C-d C-j)
ドキュメントを参照します。cider-docのJava版です。
clj-refactor.el
clj-refactorのうちよく使っているコマンドを紹介します。バージョンは2.0.0です。
なお、それぞれのコマンド名の右のかっこで書いたキーは、clj-refactorのprefixキーに続けて入力するキーを示しています。
例えば、僕はprefixキーをC-c jに割り当てているので、(ad)だとC-c j a d
と打つことになります。
- add require (ad)
新しいnamespaceをrequireするときに使います。
下のような状態になるのでTabで移動しながら入力します。
;;; before
(ns post.core
(:require [plumbing.core :as pl]))
;;; after
(ns post.core
(:require [plumbing.core :as pl]
[ :as ]))
-
clean namespace (cn)
名前空間の最初のns formにソートをかけ、使われていないものは削除してくれます。 -
wrap in thred first (tf)
入れ子関数呼び出しになっている箇所をthread first macroに変えてくれます。
;;; before
(sql/from (sql/select :*) :hoge)
;;; after
(-> :*
sql/select
(sql/from :hoge))
- wrap in thread last (tl)
入れ子関数呼び出しになっている箇所をthread last macroに変えてくれます。
;;; 初期状態
(map #(* % 2) (filter even? (range 1 10)))
;;; コマンド実行後
(->> 10
(range 1)
(filter even?)
(map #(* % 2)))
- unwind thread (uw)
threading macroを1段階解除してくれます。
例えば、上のwrap in thread first
やwrap in thread last
の実行例では実行後に最初の値が飛び出していてあまり美しくありません。そこでこれを使い、以下のように修正します。
;;; before
(->> 10
(range 1)
(filter even?)
(map #(* % 2)))
;;; after
(->> (range 1 10)
(filter even?)
(map #(* % 2)))
- destructure keys (dk)
いちいちkeyを指定して取得するコードを書いてしまった時に、それらをdestructuringを使ったコードに直してくれます。
;;; before
(let [m {:hoge "ugeee" :fuga "ogeee"}]
(str (:hoge m) (:fuga m)))
;;; after
(let [{:keys [hoge fuga]} {:hoge "ugeee" :fuga "ogeee"}]
(str hoge fuga))
paredit.el
pareditはClojureに限らず、Lisp系言語を使うときには必須となるかっこのバランスを維持してくれる拡張です。
これについてはすでに、次の素晴らしい記事があります。この記事内で解説されている機能は全て日常的に使うものばかりです。
Clojure
これは便利だな!と思った関数
get-in/assoc-in/update-in
これらはいずれもネストしたデータ構造から簡単にデータを取得したり、変更したデータを作ったりするときにとても便利な関数です。
(def m [{:name "John"
:profiles {:zip "111-1111"}}
{:name "Mickel"
:profiles {:zip "765-4321"}}])
;;; get-in
(get-in m [1 :profiles :zip])
;;=> "765-4321"
;;; assoc-in
(assoc-in m [1 :profiles] {:zip "123-4567"})
;;=> [{:name "John", :profiles {:zip "111-1111"}} {:name "Mickel", :profiles {:zip "123-4567"}}]
;;; update-in
(update-in m [0 :name] clojure.string/upper-case)
;;=> [{:name "JOHN", :profiles {:zip "111-1111"}} {:name "Mickel", :profiles {:zip "765-4321"}}]
vectorにもmapにもよしなにアクセスできていて大変素晴らしいですよね。
juxt
これも初めて知った時感動しました。
juxtについてはすでに以下の良い記事があります。
filter と remove のふたつの結果を簡単に受け取る方法
cond->/cond->>
cond->/cond->>を使うとthreading macroのそれぞれの関数適用に条件をつけることができます。
(let [condition-a true
condition-b false
condition-c nil]
(cond-> "hoge"
condition-a (str "-fuga")
condition-b (clojure.string/capitalize)
condition-c (clojure.string/split #"-")))
;;=> "hoge-fuga"
(let [condition-a true
condition-b true
condition-c nil]
(cond-> "hoge"
condition-a (str "-fuga")
condition-b (clojure.string/capitalize)
condition-c (clojure.string/split #"-")))
;;=> "Hoge-fuga"
(let [condition-a true
condition-b false
condition-c true]
(cond-> "hoge"
condition-a (str "-fuga")
condition-b (clojure.string/capitalize)
condition-c (clojure.string/split #"-")))
;;=> ["hoge" "fuga"]
開発中に参照している資料
書籍
-
Living Clojure
Clojureの入門書です。この本の素晴らしいところは説明の構成です。前から読んでいけば段階的にわかるように書かれています。今現時点でこれ以上の入門書はないでしょう。 -
Clojure Applied
他の言語でいう、Effectiveシリーズくらいのレベル感の書籍です。文法を超えて実践的にはどうすればいいのかって疑問に答えてくれます。僕もまだ読み途中です。
Web
-
CIDER Quick-Reference Card
cider-modeの機能が見やすくまとまってます。 -
Clojure Destructuring Tutorial and Cheat Sheet
destructuringについてわかりやすく解説してくれているページです。よくお世話になっています。