├── .circleci
└── config.yml
└── README.adoc
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | jobs:
4 | docs-build:
5 | docker:
6 | - image: ruby:2.6
7 | steps:
8 | - checkout
9 | - run:
10 | name: Install AsciiDoctor & Rouge
11 | command: |
12 | gem install asciidoctor
13 | gem install rouge -v 3.3.0
14 | - run:
15 | name: Build Site
16 | command: asciidoctor -a toc="left" -a toclevels=2 README.adoc -o _build/html/index.html
17 | - persist_to_workspace:
18 | root: _build
19 | paths: html
20 | docs-deploy:
21 | docker:
22 | - image: node:8.10.0
23 | steps:
24 | - checkout
25 | - attach_workspace:
26 | at: _build
27 | - run:
28 | name: Disable jekyll builds
29 | command: touch _build/html/.nojekyll
30 | - run:
31 | name: Install and configure dependencies
32 | command: |
33 | npm install -g --silent gh-pages@2.0.1
34 | git config user.email "roimisia@gmail.com"
35 | git config user.name "ci-build"
36 | - add_ssh_keys:
37 | fingerprints:
38 | - "cf:04:50:6a:b9:76:da:78:66:57:31:74:e8:35:89:6f"
39 | - run:
40 | name: Deploy docs to gh-pages branch
41 | command: gh-pages -add --dotfiles --message "[skip ci] Update site" --dist _build/html
42 |
43 | workflows:
44 | version: 2
45 | build:
46 | jobs:
47 | - docs-build
48 | - docs-deploy:
49 | requires:
50 | - docs-build
51 | filters:
52 | branches:
53 | only: ja
54 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Clojureスタイルガイド
2 | :idprefix:
3 | :idseparator: -
4 | :sectanchors:
5 | :sectlinks:
6 | :toclevels: 1
7 | ifndef::backend-pdf[]
8 | :toc-title: pass:[
目次
]
9 | endif::[]
10 | :source-highlighter: rouge
11 |
12 | このスタイルガイドは、 https://twitter.com/bbatsov[Bozhidar Batsov] 氏による https://github.com/bbatsov/clojure-style-guide[bbatsov/clojure-style-guide] の日本語翻訳版です。
13 |
14 | image:http://i.creativecommons.org/l/by/3.0/88x31.png[Creative Commons License] 本翻訳版のライセンスは http://creativecommons.org/licenses/by/3.0/deed.ja_JP[クリエイティブ・コモンズ 表示 3.0 非移植 ライセンス] とします。原著作者はBozhidar Batsov氏です。原文のライセンスについてもご注意ください。
15 |
16 | 意訳していて、原文の意味を損なわない程度に言葉を加えたり省略している部分があります。また、訳が間違っている可能性があります。逐次修正は行いますが、原文を優先するようにしてください。用語の翻訳には、 http://ssl.ohmsha.co.jp/cgi-bin/menu.cgi?ISBN=978-4-274-06913-0[プログラミングClojure第2版] を参考にしています。翻訳に対する意見や修正は、IssueやPull Requestを作成していただけると助かります。なお、規約自体に対する意見は https://github.com/bbatsov/clojure-style-guide[原文リポジトリ] にお願いします。
17 |
18 | '''
19 |
20 | == はじめに [[introduction]]
21 |
22 | [quote, アレックス・マーフィー巡査 / ロボコップ]
23 | ____
24 | 良い手本は大切だ。
25 | ____
26 |
27 | ifdef::env-github[]
28 | TIP: このスタイルガイドのナビゲーション付きの見やすいバージョンが https://totakke.github.io/clojure-style-guide/ にあります。
29 | endif::[]
30 |
31 | このClojureスタイルガイドは、実際のClojureプログラマーが他のClojureプログラマーに保守してもらえるコードを書くための、ベストプラクティスを勧めるものだ。
32 | 実際の使われ方を反映したスタイルガイドは利用されるが、理想的であっても人々に受け入れられないような規約を持つスタイルガイドは全く利用されない危険がある -- たとえそれがどれほど優れたものであったとしても。
33 |
34 | ガイドは関連する規約ごとにいくつかのセクションに分かれて構成されている。規約には理論的根拠を付けるようにした(ただし、根拠が明らかな場合は省略している)。
35 |
36 | これらの規約は、出し抜けに考えだされたものではない。これらの多くは、私のプロソフトウェアエンジニアとしての幅広い仕事や、Clojureコミュニティメンバーからのフィードバックと意見、そして、 http://www.clojurebook.com/["Clojure Programming"] や http://joyofclojure.com/["The Joy of Clojure"] のような高い評価を受けている様々なリソースに基づいている。
37 |
38 | NOTE: このスタイルガイドはまだ作成途中だ。そのため、いくつかのセクションが欠けていたり、不完全であったり、いくつかの規約には例がなかったり、それが明快でなかったりする。やがてこれらの問題は解決されていくだろうが -- 今はそれを念頭に置いてほしい。
39 |
40 | また、Clojure開発コミュニティが https://clojure.org/community/contrib_howto#_coding_guidelines[ライブラリのコーディング規約] の一覧をまとめていることも覚えておいてほしい。
41 |
42 | ifdef::env-github[]
43 | https://asciidoctor.org/docs/asciidoctor-pdf/[AsciiDoctor PDF] を利用することで、このスタイルガイドのPDF版を生成することができます。また、 https://asciidoctor.org/#installation[AsciiDoctor] を https://asciidoctor.org/docs/convert-documents/#converting-a-document-to-html[使う] ことで、HTML版を生成できます。
44 |
45 | [source,shell]
46 | ----
47 | # README.pdfの生成
48 | asciidoctor-pdf -a allow-uri-read README.adoc
49 |
50 | # README.htmlの生成
51 | asciidoctor
52 | ----
53 |
54 | [TIP]
55 | ====
56 | 生成されたドキュメント中でシンタックスハイライトをしたい場合は、 `rouge` gemをインストールしてください。
57 |
58 | [source,shell]
59 | ----
60 | gem install rouge
61 | ----
62 | ====
63 | endif::[]
64 |
65 | このスタイルガイドは以下の言語に翻訳されている:
66 |
67 | * https://github.com/bbatsov/clojure-style-guide[英語(原文)]
68 | * https://github.com/geekerzp/clojure-style-guide/blob/master/README-zhCN.md[中国語]
69 | * https://github.com/kwakbab/clojure-style-guide/blob/master/README-koKO.md[韓国語]
70 | * https://github.com/theSkilled/clojure-style-guide/blob/pt-BR/README.md[ポルトガル語] (作業中)
71 | * https://github.com/Nondv/clojure-style-guide/blob/master/ru/README.md[ロシア語]
72 | * https://github.com/jeko2000/clojure-style-guide/blob/master/README.md[スペイン語]
73 |
74 | == ソースコードのレイアウトと構造 [[source-code-layout-organization]]
75 |
76 | [quote, ジェリー・コフィン (インデントについて)]
77 | ____
78 | ほとんど全ての人は、自分のスタイル以外のあらゆるスタイルは汚くて読みにくい、と思っている。「自分のスタイル以外の」を削れば、それはおそらく正しい...
79 | ____
80 |
81 | [#80-character-limits]
82 | === 1行の最大長 [[line-length]]
83 |
84 | 可能なら、1行が80文字を超えないようにする。
85 |
86 | .なぜ現代のワイドスクリーンディスプレイで80文字なのか?
87 | ****
88 | 多くの人が、1行につき最大80文字なのは単に古い時代の名残であり、現代ではほとんど意味がないと感じている。実際、現代のディスプレイならば1行に容易に200文字以上を表示できる。しかし依然として、コードの行を短く保つことにはいくつかの利点が存在する。
89 |
90 | 何よりもまず、数多くの研究から、人間は垂直方向により早く読み進めることができ、長い行は文章を読みにくくするということがわかっている。すでに述べたように、このスタイルガイドの方針の1つは、人間が読むことを前提としたコードの最適化だ。
91 |
92 | さらに、エディタのウィンドウ幅が制限されることで複数のファイルを横並びに開いておくことができ、2つのバージョンを隣に表示するコードレビューツールを使う際に便利だ。
93 |
94 | 多くのツールのデフォルトの折返し機能は、コードの視覚的な構造を壊してしまうため、理解することがより難しくなる。たとえツールが行の最後に折り返しのマーカーを付けてくれるのだとしても、ウィンドウ幅80文字のエディタで折り返しを避けるために文字数制限が選択される。いくつかのWebベースのツールでは、動的な折返し機能を提供していないものもある。
95 |
96 | 長い行を強く好むチームもいる。1つのチームが主にメンテナンスするコードであれば、制限を100文字以内や120文字以内に増やしても良い。それでも、120文字を超える長さにすることは止めてほしい。
97 | ****
98 |
99 | === タブ vs スペース [[spaces]]
100 |
101 | インデントには *スペース* を使う。タブは使わない。
102 |
103 | === ボディのインデント [[body-indentation]]
104 |
105 | ボディパラメータをもつフォームのボディには2つのスペースを使う。これには全ての `def` フォーム、ローカル束縛をもつスペシャルフォームおよびマクロ(例: `loop`, `let`, `when-let`)、そして `when`, `cond`, `+as->+`, `+cond->+`, `case`, `with-*` などの多くのマクロが含まれる。
106 |
107 | [source,clojure]
108 | ----
109 | ;; 良い
110 | (when something
111 | (something-else))
112 |
113 | (with-out-str
114 | (println "Hello, ")
115 | (println "world!"))
116 |
117 | ;; 悪い - 4つのスペース
118 | (when something
119 | (something-else))
120 |
121 | ;; 悪い - 1つのスペース
122 | (with-out-str
123 | (println "Hello, ")
124 | (println "world!"))
125 | ----
126 |
127 | === 関数の引数の揃え方 [[vertically-align-fn-args]]
128 |
129 | 複数行にわたる関数(マクロ)の引数は左揃えにする。
130 |
131 | [source,clojure]
132 | ----
133 | ;; 良い
134 | (filter even?
135 | (range 1 10))
136 |
137 | ;; 悪い
138 | (filter even?
139 | (range 1 10))
140 | ----
141 |
142 | === 引数のインデント [[one-space-indent]]
143 |
144 | 関数(マクロ)名と同じ行に引数をもたない関数(マクロ)では、インデントには1つのスペースを用いる。
145 |
146 | [source,clojure]
147 | ----
148 | ;; 良い
149 | (filter
150 | even?
151 | (range 1 10))
152 |
153 | (or
154 | ala
155 | bala
156 | portokala)
157 |
158 | ;; 悪い - 2つのスペースによるインデント
159 | (filter
160 | even?
161 | (range 1 10))
162 |
163 | (or
164 | ala
165 | bala
166 | portokala)
167 | ----
168 |
169 | === 束縛の揃え方 [[bindings-alignment]]
170 |
171 | `let` (および `let` 系) の束縛を左揃えにする。
172 |
173 | [source,clojure]
174 | ----
175 | ;; 良い
176 | (let [thing1 "some stuff"
177 | thing2 "other stuff"]
178 | (foo thing1 thing2))
179 |
180 | ;; 悪い
181 | (let [thing1 "some stuff"
182 | thing2 "other stuff"]
183 | (foo thing1 thing2))
184 | ----
185 |
186 | === マップのキーの揃え方 [[map-keys-alignment]]
187 |
188 | マップのキーを左揃えにする。
189 |
190 | [source,clojure]
191 | ----
192 | ;; 良い
193 | {:thing1 thing1
194 | :thing2 thing2}
195 |
196 | ;; 悪い
197 | {:thing1 thing1
198 | :thing2 thing2}
199 |
200 | ;; 悪い
201 | {:thing1 thing1
202 | :thing2 thing2}
203 | ----
204 |
205 | === 改行コード [[crlf]]
206 |
207 | Unixスタイルの改行コードを使用する。 footnote:[*BSD/Solaris/Linux/OSXユーザはデフォルトで問題ないが、Windowsユーザは特に注意すること。]
208 |
209 | [TIP]
210 | ====
211 | Gitを使っているなら、次の設定を追加して、Windowsの改行コードを防ぐのもいい。
212 |
213 | [source,shell]
214 | ----
215 | $ git config --global core.autocrlf true
216 | ----
217 | ====
218 |
219 | === ファイルの最終行は改行する [[terminate-files-with-a-newline]]
220 |
221 | 各ファイルの最後は改行する。
222 |
223 | TIP: 手動で改行を入れるのではなく、エディタの設定で自動化すると良い。
224 |
225 | === 括弧のスペース [[bracket-spacing]]
226 |
227 | 開き括弧(`(`, `{`, `[`)の前の文字と、閉じ括弧(`)`, `}`, `]`)の後の文字は、括弧との間にスペースを設ける。
228 | 逆に、開き括弧とそれに続く文字、閉じ括弧と直前の文字の間にはスペースを入れない。
229 |
230 | [source,clojure]
231 | ----
232 | ;; 良い
233 | (foo (bar baz) quux)
234 |
235 | ;; 悪い
236 | (foo(bar baz)quux)
237 | (foo ( bar baz ) quux)
238 | ----
239 |
240 | === シーケンシャルコレクションのリテラルにコンマを使わない [[no-commas-for-seq-literals]]
241 |
242 | [quote, アラン・パリス]
243 | ____
244 | 構文糖衣はセミコロンのガンを引き起こす。
245 | ____
246 |
247 | シーケンシャルコレクションのリテラルの要素の間にコンマを使わない。
248 |
249 | [source,clojure]
250 | ----
251 | ;; 良い
252 | [1 2 3]
253 | (1 2 3)
254 |
255 | ;; 悪い
256 | [1, 2, 3]
257 | (1, 2, 3)
258 | ----
259 |
260 | === マップリテラルのコンマ [[opt-commas-in-map-literals]]
261 |
262 | コンマや改行を使い、マップリテラルの可読性を向上させることを検討する。
263 |
264 | [source,clojure]
265 | ----
266 | ;; 良い
267 | {:name "Bruce Wayne" :alter-ego "Batman"}
268 |
269 | ;; 良い、より読みやすい
270 | {:name "Bruce Wayne"
271 | :alter-ego "Batman"}
272 |
273 | ;; 良い、よりコンパクト
274 | {:name "Bruce Wayne", :alter-ego "Batman"}
275 | ----
276 |
277 | === 後方の括弧の集約 [[gather-trailing-parens]]
278 |
279 | 後ろ側に連続する括弧は、別々の行にせず、同じ行に含める。
280 |
281 | [source,clojure]
282 | ----
283 | ;; 良い。同じ行になっている。
284 | (when something
285 | (something-else))
286 |
287 | ;; 悪い。別の行になっている。
288 | (when something
289 | (something-else)
290 | )
291 | ----
292 |
293 | === トップレベルのフォーム間の空白行 [[empty-lines-between-top-level-forms]]
294 |
295 | トップレベルのフォームの間には1行の空白行を挟む。
296 |
297 | [source,clojure]
298 | ----
299 | ;; 良い
300 | (def x ...)
301 |
302 | (defn foo ...)
303 |
304 | ;; 悪い
305 | (def x ...)
306 | (defn foo ...)
307 |
308 | ;; 悪い
309 | (def x ...)
310 |
311 |
312 | (defn foo ...)
313 | ----
314 |
315 | 例外として、関連する `def` はまとめてしまっても良い。
316 |
317 | [source,clojure]
318 | ----
319 | ;; 良い
320 | (def min-rows 10)
321 | (def max-rows 20)
322 | (def min-cols 15)
323 | (def max-cols 30)
324 | ----
325 |
326 | === 定義フォーム内に空白行を入れない [[no-blank-lines-within-def-forms]]
327 |
328 | 関数やマクロ定義の中には空白行を入れない。ただし、 `let` や `cond` 等において、ペアをグループ分けするために入れるのは良い。
329 |
330 | === 行末の空白を避ける [[no-trailing-whitespace]]
331 |
332 | 行末の空白を避ける。
333 |
334 | === 1名前空間に1ファイル [[one-file-per-namespace]]
335 |
336 | 1つの名前空間には1つのファイルを用い、1つのファイルには1つの名前空間を用いる。
337 |
338 | [source,clojure]
339 | ----
340 | ;; 良い
341 | (ns foo.bar)
342 |
343 | ;; 悪い
344 | (ns foo.bar)
345 | (ns baz.qux)
346 |
347 | ;; 悪い
348 | (in-ns quux.quuz)
349 | (in-ns quuz.corge)
350 |
351 | ;; 悪い
352 | (ns foo.bar) もしくは (in-ns foo.bar) を複数のファイル内で用いる
353 | ----
354 |
355 | == 名前空間の定義 [[namespace-declaration]]
356 |
357 | === 単一セグメントの名前空間を使わない [[no-single-segment-namespaces]]
358 |
359 | 単一セグメントの名前空間を使わない。
360 |
361 | [source,clojure]
362 | ----
363 | ;; 良い
364 | (ns example.ns)
365 |
366 | ;; 悪い
367 | (ns example)
368 | ----
369 |
370 | === 名前空間セグメントの制限 [[namespace-segments-limit]]
371 |
372 | 無駄に長い名前空間を使わない(例えば、5セグメントを超えるような)。
373 |
374 | === 完全な `ns` フォーム [[comprehensive-ns-declaration]]
375 |
376 | 全ての名前空間は、複数の `refer`, `require`, `import` からなる `ns` フォームで始める。順序は慣習的に `refer`, `require`, `import` の順とする。
377 |
378 | [source,clojure]
379 | ----
380 | (ns examples.ns
381 | (:refer-clojure :exclude [next replace remove])
382 | (:require [clojure.string :as s :refer [blank?]])
383 | (:import java.util.Date))
384 | ----
385 |
386 | === `ns` 中の改行 [[line-break-ns-declaration]]
387 |
388 | 複数の依存を記述する場合、新しい行から書き始め、1つごとに改行しても良い。そうすることでソートが容易になり、読みやすくなる。また、依存の変更によるdiffを減らすことができる。
389 |
390 | [source,clojure]
391 | ----
392 | ;; より良い
393 | (ns examples.ns
394 | (:require
395 | [clojure.string :as s :refer [blank?]]
396 | [clojure.set :as set]
397 | [clojure.java.shell :as sh])
398 | (:import
399 | java.util.Date
400 | java.text.SimpleDateFormat
401 | [java.util.concurrent Executors
402 | LinkedBlockingQueue]))
403 |
404 | ;; 良い
405 | (ns examples.ns
406 | (:require [clojure.string :as s :refer [blank?]]
407 | [clojure.set :as set]
408 | [clojure.java.shell :as sh])
409 | (:import java.util.Date
410 | java.text.SimpleDateFormat
411 | [java.util.concurrent Executors
412 | LinkedBlockingQueue]))
413 |
414 | ;; 悪い
415 | (ns examples.ns
416 | (:require [clojure.string :as s :refer [blank?]] [clojure.set :as set] [clojure.java.shell :as sh])
417 | (:import java.util.Date java.text.SimpleDateFormat [java.util.concurrent Executors LinkedBlockingQueue]))
418 | ----
419 |
420 | === `:use` よりも `:require` が好ましい [[prefer-require-over-use]]
421 |
422 | `ns` フォームでは `:require :refer :all` よりも `:require :refer` 、それよりも `:require :as` が好ましい。また `:use` よりも `:require` が好ましい。今後新しいコードでは `:use` を非推奨とするか検討すべきだ。
423 |
424 | [source,clojure]
425 | ----
426 | ;; 良い
427 | (ns examples.ns
428 | (:require [clojure.zip :as zip]))
429 |
430 | ;; 良い
431 | (ns examples.ns
432 | (:require [clojure.zip :refer [lefts rights]]))
433 |
434 | ;; 正当な理由があれば使ってもよい
435 | (ns examples.ns
436 | (:require [clojure.zip :refer :all]))
437 |
438 | ;; 悪い
439 | (ns examples.ns
440 | (:use clojure.zip))
441 | ----
442 |
443 | === requireとimportのソート [[sort-requirements-and-imports]]
444 |
445 | `ns` フォームではrequireとimportをソートする。特にrequire/importする名前空間が非常に多い場合には、ソートすることで可読性が向上し、重複を避けられるようになる。
446 |
447 | [source,clojure]
448 | ----
449 | ;; 良い
450 | (ns examples.ns
451 | (:require
452 | [baz.core :as baz]
453 | [clojure.java.shell :as sh]
454 | [clojure.set :as set]
455 | [clojure.string :as s :refer [blank?]]
456 | [foo.bar :as foo]))
457 |
458 | ;; 悪い
459 | (ns examples.ns
460 | (:require
461 | [clojure.string :as s :refer [blank?]]
462 | [clojure.set :as set]
463 | [baz.core :as baz]
464 | [foo.bar :as foo]
465 | [clojure.java.shell :as sh]))
466 | ----
467 |
468 | == 関数 [[functions]]
469 |
470 | === 関数名の後に改行しても良い [[optional-new-line-after-fn-name]]
471 |
472 | `defn` において、ドキュメント文字列を持たない場合は、関数名と引数ベクタの間の改行を省略しても良い。
473 |
474 | [source,clojure]
475 | ----
476 | ;; 良い
477 | (defn foo
478 | [x]
479 | (bar x))
480 |
481 | ;; 良い
482 | (defn foo [x]
483 | (bar x))
484 |
485 | ;; 悪い
486 | (defn foo
487 | [x] (bar x))
488 | ----
489 |
490 | === マルチメソッドのディスパッチの位置
491 |
492 | マルチメソッドの `dispatch-val` は関数名と同じ行に置く。
493 |
494 | [source,clojure]
495 | ----
496 | ;; 良い
497 | (defmethod foo :bar [x] (baz x))
498 |
499 | (defmethod foo :bar
500 | [x]
501 | (baz x))
502 |
503 | ;; 悪い
504 | (defmethod foo
505 | :bar
506 | [x]
507 | (baz x))
508 |
509 | (defmethod foo
510 | :bar [x]
511 | (baz x))
512 | ----
513 |
514 | === 1行の短い関数 [[oneline-short-fn]]
515 |
516 | 関数本体が短い場合、引数ベクタと関数本体の間の改行は省略しても良い。
517 |
518 | [source,clojure]
519 | ----
520 | ;; 良い
521 | (defn foo [x]
522 | (bar x))
523 |
524 | ;; 関数本体が短い場合は良い
525 | (defn foo [x] (bar x))
526 |
527 | ;; マルチアリティ関数には良い
528 | (defn foo
529 | ([x] (bar x))
530 | ([x y]
531 | (if (predicate? x)
532 | (bar x)
533 | (baz x))))
534 |
535 | ;; 悪い
536 | (defn foo
537 | [x] (if (predicate? x)
538 | (bar x)
539 | (baz x)))
540 | ----
541 |
542 | === 複数アリティのインデント
543 |
544 | 関数定義における各アリティのフォームのインデントは、そのパラメータと左揃えにする。
545 |
546 | [source,clojure]
547 | ----
548 | ;; 良い
549 | (defn foo
550 | "I have two arities."
551 | ([x]
552 | (foo x 1))
553 | ([x y]
554 | (+ x y)))
555 |
556 | ;; 悪い - 過剰なインデント
557 | (defn foo
558 | "I have two arities."
559 | ([x]
560 | (foo x 1))
561 | ([x y]
562 | (+ x y)))
563 | ----
564 |
565 | === 複数アリティの順序 [[multiple-arity-order]]
566 |
567 | 関数のアリティは、引数が最も少ないものから多いものの順に並べる。マルチアリティ関数の通例として、K個の引数を持つものが関数の振る舞いを定義していて、N個(< K)の引数を持つアリティはK引数のアリティの部分適用、N個(> K)の引数を持つアリティは可変長引数であるK引数のアリティの畳み込み、という場合がある。
568 |
569 | [source,clojure]
570 | ----
571 | ;; 良い - n番目のアリティを見つけやすい
572 | (defn foo
573 | "I have two arities."
574 | ([x]
575 | (foo x 1))
576 | ([x y]
577 | (+ x y)))
578 |
579 | ;; ok - 他のアリティは2引数のアリティの適用
580 | (defn foo
581 | "I have two arities."
582 | ([x y]
583 | (+ x y))
584 | ([x]
585 | (foo x 1))
586 | ([x y z & more]
587 | (reduce foo (foo x (foo y z)) more)))
588 |
589 | ;; 悪い - 明確な理由のない順序
590 | (defn foo
591 | ([x] 1)
592 | ([x y z] (foo x (foo y z)))
593 | ([x y] (+ x y))
594 | ([w x y z & more] (reduce foo (foo w (foo x (foo y z))) more)))
595 | ----
596 |
597 | === 関数の長さ [[function-length]]
598 |
599 | 関数はLOC (lines of code)が10行を超えないようにする。理想的には、ほとんどの関数はLOCが5行より短いほうが良い。
600 |
601 | === 関数のパラメータの制限 [[function-positional-parameter-limit]]
602 |
603 | 3つか4つを超えるパラメータを持つパラメータリストの使用を避ける。
604 |
605 | === コンディションマップ [[pre-post-conditions]]
606 |
607 | 関数本体内では、コンディションマップによる入力値、出力値のチェックがより良い。
608 |
609 | [source,clojure]
610 | ----
611 | ;; 良い
612 | (defn foo [x]
613 | {:pre [(pos? x)]}
614 | (bar x))
615 |
616 | ;; 悪い
617 | (defn foo [x]
618 | (if (pos? x)
619 | (bar x)
620 | (throw (IllegalArgumentException. "x must be a positive number!")))
621 | ----
622 |
623 | == 構文 [[idioms]]
624 |
625 | === 動的な名前空間の操作 [[ns-fns-only-in-repl]]
626 |
627 | `require` や `refer` のような名前空間を扱う関数の使用を避ける。これらはREPL環境以外では必要ないものだ。
628 |
629 | === 前方参照 [[forward-references]]
630 |
631 | 前方参照を避ける。前方参照は時として必要になるが、実際にはそのような機会はまれだ。
632 |
633 | === declare [[declare]]
634 |
635 | 前方参照が必要なとき、前方参照を可能にするには `declare` を使う。
636 |
637 | === 高階関数 [[higher-order-fns]]
638 |
639 | `loop/recur` よりも `map` のように、より高階な関数のほうが好ましい。
640 |
641 | === 関数内のvar [[dont-def-vars-inside-fns]]
642 |
643 | 関数内でvarを定義しない。
644 |
645 | [source,clojure]
646 | ----
647 | ;; 非常に悪い
648 | (defn foo []
649 | (def x 5)
650 | ...)
651 | ----
652 |
653 | === `clojure.core` の名前の隠蔽 [[dont-shadow-clojure-core]]
654 |
655 | ローカル束縛によって `clojure.core` の名前を隠さない。
656 |
657 | [source,clojure]
658 | ----
659 | ;; 悪い - 関数内ではclojure.core/mapを完全修飾しなければならなくなる
660 | (defn foo [map]
661 | ...)
662 | ----
663 |
664 | === varの変更 [[alter-var]]
665 |
666 | varの値を変更するには、 `def` の代わりに `alter-var-root` を使う。
667 |
668 | [source,clojure]
669 | ----
670 | ;; 良い
671 | (def thing 1) ; thingの値は1
672 | ; thingを用いた何らかの処理
673 | (alter-var-root #'thing (constantly nil)) ; thingの値はnil
674 |
675 | ;; 悪い
676 | (def thing 1)
677 | ; thingを用いた何らかの処理
678 | (def thing nil)
679 | ; thingの値はnil
680 | ----
681 |
682 | === nil punning [[nil-punning]]
683 |
684 | シーケンスが空かどうかをチェックするには `seq` を使う(このテクニックはしばしば _nil punning_ と呼ばれる)。
685 |
686 | [source,clojure]
687 | ----
688 | ;; 良い
689 | (defn print-seq [s]
690 | (when (seq s)
691 | (prn (first s))
692 | (recur (rest s))))
693 |
694 | ;; 悪い
695 | (defn print-seq [s]
696 | (when-not (empty? s)
697 | (prn (first s))
698 | (recur (rest s))))
699 | ----
700 |
701 | === シーケンスからベクタへの変換 [[to-vector]]
702 |
703 | シーケンスをベクタに変換する必要があるときは、 `into` よりも `vec` を用いたほうが良い。
704 |
705 | [source,clojure]
706 | ----
707 | ;; 良い
708 | (vec some-seq)
709 |
710 | ;; 悪い
711 | (into [] some-seq)
712 | ----
713 |
714 | === `when` vs `if` [[when-instead-of-single-branch-if]]
715 |
716 | `+(if ... (do ...))+` の代わりに `when` を使う。
717 |
718 | [source,clojure]
719 | ----
720 | ;; 良い
721 | (when pred
722 | (foo)
723 | (bar))
724 |
725 | ;; 悪い
726 | (if pred
727 | (do
728 | (foo)
729 | (bar)))
730 | ----
731 |
732 | === `if-let` [[if-let]]
733 |
734 | `let` + `if` の代わりに `if-let` を使う。
735 |
736 | [source,clojure]
737 | ----
738 | ;; 良い
739 | (if-let [result (foo x)]
740 | (something-with result)
741 | (something-else))
742 |
743 | ;; 悪い
744 | (let [result (foo x)]
745 | (if result
746 | (something-with result)
747 | (something-else)))
748 | ----
749 |
750 | === `when-let` [[when-let]]
751 |
752 | `let` + `when` の代わりに `when-let` を使う。
753 |
754 | [source,clojure]
755 | ----
756 | ;; 良い
757 | (when-let [result (foo x)]
758 | (do-something-with result)
759 | (do-something-more-with result))
760 |
761 | ;; 悪い
762 | (let [result (foo x)]
763 | (when result
764 | (do-something-with result)
765 | (do-something-more-with result)))
766 | ----
767 |
768 | === `if-not` [[if-not]]
769 |
770 | `+(if (not ...) ...)+` の代わりに `if-not` を使う。
771 |
772 | [source,clojure]
773 | ----
774 | ;; 良い
775 | (if-not pred
776 | (foo))
777 |
778 | ;; 悪い
779 | (if (not pred)
780 | (foo))
781 | ----
782 |
783 | === `when-not` [[when-not]]
784 |
785 | `+(when (not ...) ...)+` の代わりに `when-not` を使う。
786 |
787 | [source,clojure]
788 | ----
789 | ;; 良い
790 | (when-not pred
791 | (foo)
792 | (bar))
793 |
794 | ;; 悪い
795 | (when (not pred)
796 | (foo)
797 | (bar))
798 | ----
799 |
800 | === `when-not` vs `if-not` [[when-not-instead-of-single-branch-if-not]]
801 |
802 | `(if-not ... (do ...))` の代わりに `when-not` を使う。
803 |
804 | [source,clojure]
805 | ----
806 | ;; 良い
807 | (when-not pred
808 | (foo)
809 | (bar))
810 |
811 | ;; 悪い
812 | (if-not pred
813 | (do
814 | (foo)
815 | (bar)))
816 | ----
817 |
818 | === `not=` [[not-equal]]
819 |
820 | `(not (= ...))` の代わりに `not=` を使う。
821 |
822 | [source,clojure]
823 | ----
824 | ;; 良い
825 | (not= foo bar)
826 |
827 | ;; 悪い
828 | (not (= foo bar))
829 | ----
830 |
831 | === `printf` [[printf]]
832 |
833 | `(print (format ...))` の代わりに `printf` を使う。
834 |
835 | [source,clojure]
836 | ----
837 | ;; 良い
838 | (printf "Hello, %s!\n" name)
839 |
840 | ;; ok
841 | (println (format "Hello, %s!" name))
842 | ----
843 |
844 | === 柔軟な比較関数 [[multiple-arity-of-gt-and-ls-fns]]
845 |
846 | 比較を行うときは、Clojure関数の `<` や `>` などは可変長引数を許していることを覚えておこう。
847 |
848 | [source,clojure]
849 | ----
850 | ;; 良い
851 | (< 5 x 10)
852 |
853 | ;; 悪い
854 | (and (> x 5) (< x 10))
855 | ----
856 |
857 | === 単一パラメータの関数リテラル [[single-param-fn-literal]]
858 |
859 | ただ1つのパラメータを持つ関数リテラルでは、 `%1` よりも `%` のほうが好ましい。
860 |
861 | [source,clojure]
862 | ----
863 | ;; 良い
864 | #(Math/round %)
865 |
866 | ;; 悪い
867 | #(Math/round %1)
868 | ----
869 |
870 | === 複数パラメータの関数リテラル [[multiple-params-fn-literal]]
871 |
872 | 複数のパラメータを持つ関数リテラルでは、 `%` よりも `%1` のほうが好ましい。
873 |
874 | [source,clojure]
875 | ----
876 | ;; 良い
877 | #(Math/pow %1 %2)
878 |
879 | ;; 悪い
880 | #(Math/pow % %2)
881 | ----
882 |
883 | === 無意味な無名関数を使用しない [[no-useless-anonymous-fns]]
884 |
885 | 必要ないなら無名関数でラップしない。
886 |
887 | [source,clojure]
888 | ----
889 | ;; 良い
890 | (filter even? (range 1 10))
891 |
892 | ;; 悪い
893 | (filter #(even? %) (range 1 10))
894 | ----
895 |
896 | === 複数フォームの関数リテラルを使用しない [[no-multiple-forms-fn-literals]]
897 |
898 | 関数本体が2つ以上のフォームを含む場合は、関数リテラルを使用しない。
899 |
900 | [source,clojure]
901 | ----
902 | ;; 良い
903 | (fn [x]
904 | (println x)
905 | (* x 2))
906 |
907 | ;; 悪い (doフォームを明示的に使わなければならない)
908 | #(do (println %)
909 | (* % 2))
910 | ----
911 |
912 | === `complement` [[complement]]
913 |
914 | 無名関数よりも `complement` を用いたほうが良い。
915 |
916 | [source,clojure]
917 | ----
918 | ;; 良い
919 | (filter (complement some-pred?) coll)
920 |
921 | ;; 悪い
922 | (filter #(not (some-pred? %)) coll)
923 | ----
924 |
925 | この規約は、反対の述語が別の関数としてある場合は無視するべきだ。(例: `even?` と `odd?` )
926 |
927 | === `comp` [[comp]]
928 |
929 | 関数合成するには、無名関数よりも `comp` が好ましい。
930 |
931 | [source,clojure]
932 | ----
933 | ;; `(:require [clojure.string :as str])` を仮定して...
934 |
935 | ;; 良い
936 | (map #(str/capitalize (str/trim %)) ["top " " test "])
937 |
938 | ;; より良い
939 | (map (comp str/capitalize str/trim) ["top " " test "])
940 | ----
941 |
942 | === `partial` [[partial]]
943 |
944 | カリー化するには、無名関数よりも `partial` が好ましい。
945 |
946 | [source,clojure]
947 | ----
948 | ;; 良い
949 | (map #(+ 5 %) (range 1 10))
950 |
951 | ;; (きっと) より良い
952 | (map (partial + 5) (range 1 10))
953 | ----
954 |
955 | === スレッディングマクロ [[threading-macros]]
956 |
957 | 深いネストよりもスレッディングマクロ `+->+` (thread-first)と `+->>+` (thread-last)の使用が好ましい。
958 |
959 | [source,clojure]
960 | ----
961 | ;; 良い
962 | (-> [1 2 3]
963 | reverse
964 | (conj 4)
965 | prn)
966 |
967 | ;; あまり良くない
968 | (prn (conj (reverse [1 2 3])
969 | 4))
970 |
971 | ;; 良い
972 | (->> (range 1 10)
973 | (filter even?)
974 | (map (partial * 2)))
975 |
976 | ;; あまり良くない
977 | (map (partial * 2)
978 | (filter even? (range 1 10)))
979 | ----
980 |
981 | === スレッディングマクロの揃え方
982 |
983 | スレッディングマクロ `+->+` (thread-first)と `+->>+` (thread-last) の引数は揃える。
984 |
985 | [source,clojure]
986 | ----
987 | ;; 良い
988 | (->> (range)
989 | (filter even?)
990 | (take 5))
991 |
992 | ;; 悪い
993 | (->> (range)
994 | (filter even?)
995 | (take 5))
996 | ----
997 |
998 | === `cond` のデフォルト条件 [[else-keyword-in-cond]]
999 |
1000 | `cond` で残り全ての条件をキャッチするときは `:else` を使う。
1001 |
1002 | [source,clojure]
1003 | ----
1004 | ;; 良い
1005 | (cond
1006 | (neg? n) "negative"
1007 | (pos? n) "positive"
1008 | :else "zero")
1009 |
1010 | ;; 悪い
1011 | (cond
1012 | (neg? n) "negative"
1013 | (pos? n) "positive"
1014 | true "zero")
1015 | ----
1016 |
1017 | === `condp` vs `cond` [[condp]]
1018 |
1019 | 述語と式が変わらない場合、 `cond` よりも `condp` のほうが良い。
1020 |
1021 | [source,clojure]
1022 | ----
1023 | ;; 良い
1024 | (cond
1025 | (= x 10) :ten
1026 | (= x 20) :twenty
1027 | (= x 30) :thirty
1028 | :else :dunno)
1029 |
1030 | ;; より良い
1031 | (condp = x
1032 | 10 :ten
1033 | 20 :twenty
1034 | 30 :thirty
1035 | :dunno)
1036 | ----
1037 |
1038 | === `case` vs `cond/condp` [[case]]
1039 |
1040 | テスト式がコンパイル時に固定の場合、 `cond` や `condp` の代わりに `case` を使うのが良い。
1041 |
1042 | [source,clojure]
1043 | ----
1044 | ;; 良い
1045 | (cond
1046 | (= x 10) :ten
1047 | (= x 20) :twenty
1048 | (= x 30) :forty
1049 | :else :dunno)
1050 |
1051 | ;; より良い
1052 | (condp = x
1053 | 10 :ten
1054 | 20 :twenty
1055 | 30 :forty
1056 | :dunno)
1057 |
1058 | ;; 最も良い
1059 | (case x
1060 | 10 :ten
1061 | 20 :twenty
1062 | 30 :forty
1063 | :dunno)
1064 | ----
1065 |
1066 | === cond内は短いフォームで [[short-forms-in-cond]]
1067 |
1068 | `cond` などの中では短いフォームを用いる。それが無理なら、コメントや空白行を使用して、ペアグループを見えやすくする。
1069 |
1070 | [source,clojure]
1071 | ----
1072 | ;; 良い
1073 | (cond
1074 | (test1) (action1)
1075 | (test2) (action2)
1076 | :else (default-action))
1077 |
1078 | ;; まあ良い
1079 | (cond
1080 | ;; test case 1
1081 | (test1)
1082 | (long-function-name-which-requires-a-new-line
1083 | (complicated-sub-form
1084 | (-> 'which-spans multiple-lines)))
1085 |
1086 | ;; test case 2
1087 | (test2)
1088 | (another-very-long-function-name
1089 | (yet-another-sub-form
1090 | (-> 'which-spans multiple-lines)))
1091 |
1092 | :else
1093 | (the-fall-through-default-case
1094 | (which-also-spans 'multiple
1095 | 'lines)))
1096 | ----
1097 |
1098 | === 述語としてのセット [[set-as-predicate]]
1099 |
1100 | `set` を述語として使うことができる。
1101 |
1102 | [source,clojure]
1103 | ----
1104 | ;; 良い
1105 | (remove #{1} [0 1 2 3 4 5])
1106 |
1107 | ;; 悪い
1108 | (remove #(= % 1) [0 1 2 3 4 5])
1109 |
1110 | ;; 良い
1111 | (count (filter #{\a \e \i \o \u} "mary had a little lamb"))
1112 |
1113 | ;; 悪い
1114 | (count (filter #(or (= % \a)
1115 | (= % \e)
1116 | (= % \i)
1117 | (= % \o)
1118 | (= % \u))
1119 | "mary had a little lamb"))
1120 | ----
1121 |
1122 | === `inc` と `dec` [[inc-and-dec]]
1123 |
1124 | `(+ x 1)` や `(- x 1)` の代わりに `(inc x)` や `(dec x)` を使う。
1125 |
1126 | === `pos?` と `neg?` [[pos-and-neg]]
1127 |
1128 | `(> x 0)`, `(< x 0)`, `(= x 0)` の代わりに `(pos? x)`, `(neg? x)`, `(zero? x)` を使う。
1129 |
1130 | === `list*` vs `cons` [[list-star-instead-of-nested-cons]]
1131 |
1132 | ネストされた `cons` を呼び出す代わりに `list*` を使う。
1133 |
1134 | [source,clojure]
1135 | ----
1136 | ;; 良い
1137 | (list* 1 2 3 [4 5])
1138 |
1139 | ;; 悪い
1140 | (cons 1 (cons 2 (cons 3 [4 5])))
1141 | ----
1142 |
1143 | === 糖衣されたJava呼び出し [[sugared-java-interop]]
1144 |
1145 | 糖衣されたJava呼び出しフォームを用いる。
1146 |
1147 | [source,clojure]
1148 | ----
1149 | ;;; オブジェクト生成
1150 | ;; 良い
1151 | (java.util.ArrayList. 100)
1152 |
1153 | ;; 悪い
1154 | (new java.util.ArrayList 100)
1155 |
1156 | ;;; 静的メソッドの呼び出し
1157 | ;; 良い
1158 | (Math/pow 2 10)
1159 |
1160 | ;; 悪い
1161 | (. Math pow 2 10)
1162 |
1163 | ;;; インスタンスメソッドの呼び出し
1164 | ;; 良い
1165 | (.substring "hello" 1 3)
1166 |
1167 | ;; 悪い
1168 | (. "hello" substring 1 3)
1169 |
1170 | ;;; 静的フィールドへのアクセス
1171 | ;; 良い
1172 | Integer/MAX_VALUE
1173 |
1174 | ;; 悪い
1175 | (. Integer MAX_VALUE)
1176 |
1177 | ;;; インスタンスフィールドへのアクセス
1178 | ;; 良い
1179 | (.someField some-object)
1180 |
1181 | ;; 悪い
1182 | (. some-object someField)
1183 | ----
1184 |
1185 | === trueフラグには簡易メタデータ表記 [[compact-metadata-notation-for-true-flags]]
1186 |
1187 | キーがキーワード、値がブール値 `true` のスロットしか持たないメタデータには、簡易メタデータ表記を使う。
1188 |
1189 | [source,clojure]
1190 | ----
1191 | ;; 良い
1192 | (def ^:private a 5)
1193 |
1194 | ;; 悪い
1195 | (def ^{:private true} a 5)
1196 | ----
1197 |
1198 | === プライベート [[private]]
1199 |
1200 | コード中のプライベート部分には印を付ける。
1201 |
1202 | [source,clojure]
1203 | ----
1204 | ;; 良い
1205 | (defn- private-fun [] ...)
1206 |
1207 | (def ^:private private-var ...)
1208 |
1209 | ;; 悪い
1210 | (defn private-fun [] ...) ; 全くプライベートでない
1211 |
1212 | (defn ^:private private-fun [] ...) ; 冗長な記述だ
1213 |
1214 | (def private-var ...) ; 全くプライベートでない
1215 | ----
1216 |
1217 | === プライベートなvarへのアクセス [[access-private-var]]
1218 |
1219 | (例えばテストのために)プライベートなvarにアクセスするには、 `@#'some.ns/var` フォームを使う。
1220 |
1221 | === メタデータ付加は慎重に [[attach-metadata-carefully]]
1222 |
1223 | メタデータを何に付加するかについては、よく注意したほうが良い。
1224 |
1225 | [source,clojure]
1226 | ----
1227 | ;; `a` で参照されるvarにメタデータを付加している
1228 | (def ^:private a {})
1229 | (meta a) ;=> nil
1230 | (meta #'a) ;=> {:private true}
1231 |
1232 | ;; 空のハッシュマップ値にメタデータを付加している
1233 | (def a ^:private {})
1234 | (meta a) ;=> {:private true}
1235 | (meta #'a) ;=> nil
1236 | ----
1237 |
1238 | == 命名規約 [[naming]]
1239 |
1240 | [quote, フィル・カールトン]
1241 | ____
1242 | プログラミングで本当に難しいのは、キャッシュの無効化と命名の仕方だけだ。
1243 | ____
1244 |
1245 | === 名前空間の命名方法 [[ns-naming-schemas]]
1246 |
1247 | 名前空間は次の2つの名づけ方が好ましい。
1248 |
1249 | * `project.module`
1250 | * `organization.project.module`
1251 |
1252 | === 名前空間はlisp-caseで [[lisp-case-ns]]
1253 |
1254 | 複数単語からなる名前空間セグメントには `lisp-case` を使う(例: `bruce.project-euler` )
1255 |
1256 | === lisp-case [[lisp-case]]
1257 |
1258 | 関数名や変数名には `lisp-case` を使う。
1259 |
1260 | [source,clojure]
1261 | ----
1262 | ;; 良い
1263 | (def some-var ...)
1264 | (defn some-fun ...)
1265 |
1266 | ;; 悪い
1267 | (def someVar ...)
1268 | (defn somefun ...)
1269 | (def some_fun ...)
1270 | ----
1271 |
1272 | === プロトコル、レコード、構造体、型はCamelCaseで [[CamelCase-for-protocols-records-structs-and-types]]
1273 |
1274 | プロトコル、レコード、構造体、型には `CamelCase` を用いる。(HTTP, RFC, XMLのような頭字語は大文字を保持する。)
1275 |
1276 | === 述語にはクエスチョンマークを用いる [[pred-with-question-mark]]
1277 |
1278 | 述語(ブール値を返す関数)の名前はクエスチョンマーク(?)で終わるべきだ。(例: `even?` )
1279 |
1280 | [source,clojure]
1281 | ----
1282 | ;; 良い
1283 | (defn palindrome? ...)
1284 |
1285 | ;; 悪い
1286 | (defn palindrome-p ...) ; Common Lispスタイル
1287 | (defn is-palindrome ...) ; Javaスタイル
1288 | ----
1289 |
1290 | === 状態を変える関数にはエクスクラメーションマークを用いる [[changing-state-fns-with-exclamation-mark]]
1291 |
1292 | STMトランザクションの中で安全でない関数・マクロの名前はエクスクラメーションマーク(!)で終わるべきだ。(例: `reset!` )
1293 |
1294 | === toの代わりに矢印 [[arrow-instead-of-to]]
1295 |
1296 | 変換のための関数名には `to` ではなく `+->+` を用いる。
1297 |
1298 | [source,clojure]
1299 | ----
1300 | ;; 良い
1301 | (defn f->c ...)
1302 |
1303 | ;; あまり良くない
1304 | (defn f-to-c ...)
1305 | ----
1306 |
1307 | === dynamicなvarには耳あてを [[earmuffs-for-dynamic-vars]]
1308 |
1309 | 再束縛を想定しているものには `*earmuffs*` を使う(つまりdynamicなものだ)。
1310 |
1311 | [source,clojure]
1312 | ----
1313 | ;; 良い
1314 | (def ^:dynamic *a* 10)
1315 |
1316 | ;; 悪い
1317 | (def ^:dynamic a 10)
1318 | ----
1319 |
1320 | === 定数に特別な表記をしない [[dont-flag-constants]]
1321 |
1322 | 定数のために特別な表記をしない。特定のものを除いて、全ては定数である。
1323 |
1324 | === 使用しない束縛にはアンダースコア [[underscore-for-unused-bindings]]
1325 |
1326 | 直後のコードで使用されない分配束縛や引数名には `+_+` を使う。
1327 |
1328 | [source,clojure]
1329 | ----
1330 | ;; 良い
1331 | (let [[a b _ c] [1 2 3 4]]
1332 | (println a b c))
1333 |
1334 | (dotimes [_ 3]
1335 | (println "Hello!"))
1336 |
1337 | ;; 悪い
1338 | (let [[a b c d] [1 2 3 4]]
1339 | (println a b d))
1340 |
1341 | (dotimes [i 3]
1342 | (println "Hello!"))
1343 | ----
1344 |
1345 | コードの理解を助けるためならば、使用しない引数や分配束縛のマップに明示的に名前を付けても良い。この場合、その変数が実際には使われないことを示すため、先頭にアンダースコアを付加する。
1346 |
1347 | [source,clojure]
1348 | ----
1349 | ;; 良い
1350 | (defn myfun1 [context _]
1351 | (assoc context :foo "bar"))
1352 |
1353 | (defn myfun2 [context {:keys [id]}]
1354 | (assoc context :user-id id))
1355 |
1356 | ;; より良い
1357 | (defn myfun1 [context _user]
1358 | (assoc context :foo "bar"))
1359 |
1360 | (defn myfun2 [context {:keys [id] :as _user}]
1361 | (assoc context :user-id id))
1362 | ----
1363 |
1364 | === 慣用名
1365 |
1366 | `pred` や `coll` のような慣用名には `clojure.core` の例が参考になる。
1367 |
1368 | * 関数内では、
1369 | ** `f`, `g`, `h` - 関数入力
1370 | ** `n` - サイズを示す整数値
1371 | ** `index`, `i` - 整数のインデックス
1372 | ** `x`, `y` - 数値
1373 | ** `xs` - シーケンス
1374 | ** `m` - マップ
1375 | ** `s` - 文字列入力
1376 | ** `re` - 正規表現
1377 | ** `coll` - コレクション
1378 | ** `pred` - 述語クロージャ
1379 | ** `& more` - 可変長引数
1380 | ** `xf` - xform、transducer
1381 | * マクロ内では、
1382 | ** `expr` - 式
1383 | ** `body` - マクロ本体
1384 | ** `binding` - マクロの束縛ベクタ
1385 |
1386 | == データ構造 [[data-structures]]
1387 |
1388 | [quote, アラン・パリス]
1389 | ____
1390 | 10種のデータ構造を処理できる機能を10個用意するより、1種のデータ構造を処理できる機能を100個用意した方が良い。
1391 | ____
1392 |
1393 | === リストを避ける [[avoid-lists]]
1394 |
1395 | 汎用的なデータ置き場としてリストを使うことを避ける(リストが本当に必要な場合を除く)。
1396 |
1397 | === マップのキーにはキーワードを用いる [[keywords-for-hash-keys]]
1398 |
1399 | マップのキーにはキーワードを用いたほうが良い。
1400 |
1401 | [source,clojure]
1402 | ----
1403 | ;; 良い
1404 | {:name "Bruce" :age 30}
1405 |
1406 | ;; 悪い
1407 | {"name" "Bruce" "age" 30}
1408 | ----
1409 |
1410 | === コレクションのリテラル構文 [[literal-col-syntax]]
1411 |
1412 | 可能なら、コレクションのリテラル構文を用いたほうが良い。ただしセットを定義するときは、コンパイル時に定数である値についてのみリテラル構文を使用する。
1413 |
1414 | [source,clojure]
1415 | ----
1416 | ;; 良い
1417 | [1 2 3]
1418 | #{1 2 3}
1419 | (hash-set (func1) (func2)) ; 実行時に決定する値
1420 |
1421 | ;; 悪い
1422 | (vector 1 2 3)
1423 | (hash-set 1 2 3)
1424 | #{(func1) (func2)} ; もし (func1) = (func2) だったら実行時例外が投げられる
1425 | ----
1426 |
1427 | === コレクションにインデックスでアクセスすることを避ける [[avoid-index-based-coll-access]]
1428 |
1429 | 可能なら、コレクションの要素にインデックスでアクセスすることを避ける。
1430 |
1431 | === マップから値を取得する関数としてのキーワード [[keywords-as-fn-to-get-map-values]]
1432 |
1433 | 可能なら、マップから値を取得する関数としてキーワードを用いるのが良い。
1434 |
1435 | [source,clojure]
1436 | ----
1437 | (def m {:name "Bruce" :age 30})
1438 |
1439 | ;; 良い
1440 | (:name m)
1441 |
1442 | ;; 必要以上の記述だ
1443 | (get m :name)
1444 |
1445 | ;; 悪い - NullPointerExceptionが発生する可能性が高い
1446 | (m :name)
1447 | ----
1448 |
1449 | === 関数としてのコレクション [[colls-as-fns]]
1450 |
1451 | ほとんどのコレクションはその要素の関数であることを活用する。
1452 |
1453 | [source,clojure]
1454 | ----
1455 | ;; 良い
1456 | (filter #{\a \e \o \i \u} "this is a test")
1457 |
1458 | ;; 悪い - 汚すぎて書けない
1459 | ----
1460 |
1461 | === 関数としてのキーワード [[keywords-as-fns]]
1462 |
1463 | キーワードはコレクションの関数として使えることを活用する。
1464 |
1465 | [source,clojure]
1466 | ----
1467 | ((juxt :a :b) {:a "ala" :b "bala"})
1468 | ----
1469 |
1470 | === 一時的コレクションを避ける [[avoid-transient-colls]]
1471 |
1472 | パフォーマンス問題がクリティカルとなる部分を除いて、一時的(transient)コレクションの使用を避ける。
1473 |
1474 | === Javaのコレクションを避ける [[avoid-java-colls]]
1475 |
1476 | Javaのコレクションの使用を避ける。
1477 |
1478 | === Javaの配列を避ける [[avoid-java-arrays]]
1479 |
1480 | Java呼び出しや、プリミティブ型を多用するパフォーマンスクリティカルなコードを除いて、Javaの配列の使用を避ける。
1481 |
1482 | == タイプとレコード [[types-records]]
1483 |
1484 | === レコードのコンストラクタ [[record-constructors]]
1485 |
1486 | タイプやレコードのインスタンスを作るのにJava呼び出しを用いない。 `deftype` や `defrecord` が自動的に生成したコンストラクタ関数を使用する。そうすることで、 `deftype` や `defrecord` を利用していることが明確になる。詳しくは https://stuartsierra.com/2015/05/17/clojure-record-constructors[この記事] を参照する。
1487 |
1488 | [source,clojure]
1489 | ----
1490 | (defrecord Foo [a b])
1491 | (deftype Bar [a b])
1492 |
1493 | ;; 良い
1494 | (->Foo 1 2)
1495 | (map->Foo {:b 4 :a 3})
1496 | (->Bar 1 2)
1497 |
1498 | ;; 悪い
1499 | (Foo. 1 2)
1500 | (Bar. 1 2)
1501 | ----
1502 |
1503 | `deftype` は `+map->Type+` というコンストラクタを作らないことに注意する。レコードでのみ使用できる。
1504 |
1505 | === カスタムレコードコンストラクタ [[custom-record-constructors]]
1506 |
1507 | 必要なら独自のタイプ/レコードのコンストラクタを追加する(例:レコード生成時にプロパティのバリデーションを行うため)。詳しくは https://stuartsierra.com/2015/05/17/clojure-record-constructors[この記事] を参照する。
1508 |
1509 | [source,clojure]
1510 | ----
1511 | (defrecord Customer [id name phone email])
1512 |
1513 | (defn make-customer
1514 | "Creates a new customer record."
1515 | [{:keys [name phone email]}]
1516 | {:pre [(string? name)
1517 | (valid-phone? phone)
1518 | (valid-email? email)]}
1519 | (->Customer (next-id) name phone email))
1520 | ----
1521 |
1522 | このようなカスタムコンストラクタには、好きな命名規則や構造を用いて構わない。
1523 |
1524 | === カスタムレコードコンストラクタの命名 [[custom-record-constructors-naming]]
1525 |
1526 | 自動生成されたタイプ/レコードのコンストラクタ関数を上書きしない。それらのコンストラクタ関数は特定の振る舞いをすると想定されているため、この挙動を変更することは驚き最小の原則に反する。詳しくは https://stuartsierra.com/2015/05/17/clojure-record-constructors[この記事] を参照する。
1527 |
1528 | [source,clojure]
1529 | ----
1530 | (defrecord Foo [num])
1531 |
1532 | ;; 良い
1533 | (defn make-foo
1534 | [num]
1535 | {:pre [(pos? num)]}
1536 | (->Foo num))
1537 |
1538 | ;; 悪い
1539 | (defn ->Foo
1540 | [num]
1541 | {:pre [(pos? num)]}
1542 | (Foo. num))
1543 | ----
1544 |
1545 | == 状態 [[mutation]]
1546 |
1547 | === ref [[Refs]]
1548 |
1549 | ==== `io!` マクロ [[refs-io-macro]]
1550 |
1551 | トランザクションの中で思いがけずI/Oコールを呼んでしまったときの問題を回避するため、全てのI/Oコールを `io!` マクロでラップすることを考える。
1552 |
1553 | ==== `ref-set` を避ける [[refs-avoid-ref-set]]
1554 |
1555 | 出来る限り `ref-set` は使用しない。
1556 |
1557 | [source,clojure]
1558 | ----
1559 | (def r (ref 0))
1560 |
1561 | ;; 良い
1562 | (dosync (alter r + 5))
1563 |
1564 | ;; 悪い
1565 | (dosync (ref-set r 5))
1566 | ----
1567 |
1568 | ==== 小さいトランザクション [[refs-small-transactions]]
1569 |
1570 | トランザクションのサイズ(包んでいる処理の量)を出来る限り小さく保つようにする。
1571 |
1572 | ==== 同一refに対する長短期トランザクションの混在を避ける [[refs-avoid-short-long-transactions-with-same-ref]]
1573 |
1574 | 同一のrefとやり取りを行う、短期のトランザクションと長期のトランザクションを両方持つことを避ける。
1575 |
1576 | === エージェント [[Agents]]
1577 |
1578 | ==== エージェントのsend [[agents-send]]
1579 |
1580 | それがCPUバウンドで、かつI/Oや他スレッドをブロックしない処理のときだけ `send` を用いる。
1581 |
1582 | ==== エージェントのsend-off [[agents-send-off]]
1583 |
1584 | それがスレッドをブロック、スリープさせたり、そうでなくても停滞させるかもしれない処理には `send-off` を用いる。
1585 |
1586 | === アトム [[Atoms]]
1587 |
1588 | ==== トランザクション内で更新しない [[atoms-no-update-within-transactions]]
1589 |
1590 | STMトランザクションの中でアトムを更新することを避ける。
1591 |
1592 | ==== `reset!` よりも `swap!` が好ましい [[atoms-prefer-swap-over-reset]]
1593 |
1594 | 可能なら、 `reset!` よりも `swap!` を使うようにする。
1595 |
1596 | [source,clojure]
1597 | ----
1598 | (def a (atom 0))
1599 |
1600 | ;; 良い
1601 | (swap! a + 5)
1602 |
1603 | ;; あまり良くない
1604 | (reset! a 5)
1605 | ----
1606 |
1607 | == 文字列 [[strings]]
1608 |
1609 | === Java呼び出しよりもClojureの文字列関数 [[prefer-clojure-string-over-interop]]
1610 |
1611 | 文字列処理は、Java呼び出しや独自実装よりも、 `clojure.string` の関数を使うほうが好ましい。
1612 |
1613 | [source,clojure]
1614 | ----
1615 | ;; 良い
1616 | (clojure.string/upper-case "bruce")
1617 |
1618 | ;; 悪い
1619 | (.toUpperCase "bruce")
1620 | ----
1621 |
1622 | == 例外 [[exceptions]]
1623 |
1624 | === 既存の例外型の再利用 [[reuse-existing-exception-types]]
1625 |
1626 | 既存の例外型を再利用する。慣用的なClojureコードでは、例外を投げるとき、基本的な例外型を用いている(例: `java.lang.IllegalArgumentException`, `java.lang.UnsupportedOperationException`, `java.lang.IllegalStateException`, `java.io.IOException`)。
1627 |
1628 | === `finally` よりも `with-open` が好ましい [[prefer-with-open-over-finally]]
1629 |
1630 | `finally` よりも `with-open` のほうが好ましい。
1631 |
1632 | == マクロ [[macros]]
1633 |
1634 | === 関数でできるならマクロを書かない [[dont-write-macro-if-fn-will-do]]
1635 |
1636 | その処理が関数でできるならマクロを書かない。
1637 |
1638 | === マクロ書く前に使い方を書く [[write-macro-usage-before-writing-the-macro]]
1639 |
1640 | まずマクロの使用例を作成し、その後でマクロを作る。
1641 |
1642 | === 複雑なマクロの分割 [[break-complicated-macros]]
1643 |
1644 | 可能なら、複雑なマクロはより小さい機能に分割する。
1645 |
1646 | === 構文糖衣としてのマクロ [[macros-as-syntactic-sugar]]
1647 |
1648 | マクロは通常、構文糖衣を提供するものであるべきで、そのコアは単純な機能であるべきだ。そうすることでより構造化されるだろう。
1649 |
1650 | === 構文クオート [[syntax-quoted-forms]]
1651 |
1652 | 自分でリストを組み立てるよりも、構文クオートを使用するほうが好ましい。
1653 |
1654 | == コメント [[comments]]
1655 |
1656 | [quote, スティーブ・マコネル]
1657 | ____
1658 | 良いコードとは、それ自体が最良のドキュメントになっているものだ。コメントを付けようとしたとき、自分の胸に聞いてみるといい、「どうやってコードを改良して、このコメントを不要にできるだろうか?」ってね。より美しくするために、コードを改良してからドキュメント化するんだ。
1659 | ____
1660 |
1661 | === コード自体がドキュメント [[self-documenting-code]]
1662 |
1663 | 出来る限り、コードを見れば何をしているのかわかるように努める。
1664 |
1665 | === ヘッダーコメントには4つのセミコロン [[four-semicolons-for-heading-comments]]
1666 |
1667 | ヘッダーコメントには最低4つのセミコロンを用いる。
1668 |
1669 | === トップレベルのコメントには3つのセミコロン [[three-semicolons-for-top-level-comments]]
1670 |
1671 | トップレベルのコメントには3つのセミコロンを用いる。
1672 |
1673 | === コード部分には2つのセミコロン [[two-semicolons-for-code-fragment]]
1674 |
1675 | 特定のコード部分の直前にコメントを書くときは、コード部分とインデントを揃え、2つのセミコロンを用いる。
1676 |
1677 | === 行末コメントには1つのセミコロン [[one-semicolon-for-margin-comments]]
1678 |
1679 | 行末コメントには1つのセミコロンを用いる。
1680 |
1681 | === セミコロンのスペース [[semicolon-space]]
1682 |
1683 | セミコロンとそれに続くテキストの間には、常に少なくとも1つのスペースを入れる。
1684 |
1685 | [source,clojure]
1686 | ----
1687 | ;;;; Frob Grovel
1688 |
1689 | ;;; This section of code has some important implications:
1690 | ;;; 1. Foo.
1691 | ;;; 2. Bar.
1692 | ;;; 3. Baz.
1693 |
1694 | (defn fnord [zarquon]
1695 | ;; If zob, then veeblefitz.
1696 | (quux zot
1697 | mumble ; Zibblefrotz.
1698 | frotz))
1699 | ----
1700 |
1701 | === 英語の文法 [[english-syntax]]
1702 |
1703 | 2単語以上のコメントは大文字で始め、句読点を用いる。各文は http://en.wikipedia.org/wiki/Sentence_spacing[1つのスペース] で分ける。
1704 |
1705 | === 無意味なコメント [[no-superfluous-comments]]
1706 |
1707 | 無意味なコメントを避ける。
1708 |
1709 | [source,clojure]
1710 | ----
1711 | ;; 悪い
1712 | (inc counter) ; increments counter by one
1713 | ----
1714 |
1715 | === コメントの更新 [[comment-upkeep]]
1716 |
1717 | コメントは常に更新していなければならない。古いコメントは、コメントがないことよりも害悪だ。
1718 |
1719 | === `#_` リーダマクロ [[dash-underscore-reader-macro]]
1720 |
1721 | 特定のフォームをコメントアウトする必要があるときは、通常のコメントではなく `#_` リーダマクロを用いたほうが良い。
1722 |
1723 | [source,clojure]
1724 | ----
1725 | ;; 良い
1726 | (+ foo #_(bar x) delta)
1727 |
1728 | ;; 悪い
1729 | (+ foo
1730 | ;; (bar x)
1731 | delta)
1732 | ----
1733 |
1734 | === コメントよりリファクタリング [[refactor-dont-comment]]
1735 |
1736 | [quote, ラス・オルセン]
1737 | ____
1738 | 良いコードというのは面白いジョークのようなものだ。説明する必要がない。
1739 | ____
1740 |
1741 | 悪いコードを説明するためにコメントを書くことを避ける。コードをリファクタリングして、コメントが不要なようにするべきだ。(「やるか、やらぬかだ。やってみるなどない」 -- ヨーダ)
1742 |
1743 | === コメントアノテーション [[comment-annotations]]
1744 |
1745 | ==== アノテーションは直前に [[annotate-above]]
1746 |
1747 | アノテーションは通常、当該コードの直前に書かれるべきだ。
1748 |
1749 | [source,clojure]
1750 | ----
1751 | ;; 良い
1752 | (defn some-fun
1753 | []
1754 | ;; FIXME: Replace baz with the newer bar.
1755 | (baz))
1756 |
1757 | ;; 悪い
1758 | ;; FIXME: Replace baz with the newer bar.
1759 | (defn some-fun
1760 | []
1761 | (baz))
1762 | ----
1763 |
1764 | ==== アノテーションキーワード [[annotate-keywords]]
1765 |
1766 | アノテーションキーワードの後にはコロンとスペースを入れ、その後で詳細を書く。
1767 |
1768 | [source,clojure]
1769 | ----
1770 | ;; 良い
1771 | (defn some-fun
1772 | []
1773 | ;; FIXME: Replace baz with the newer bar.
1774 | (baz))
1775 |
1776 | ;; 悪い - アノテーションの後にコロンがない
1777 | (defn some-fun
1778 | []
1779 | ;; FIXME Replace baz with the newer bar.
1780 | (baz))
1781 |
1782 | ;; 悪い - コロンの後にスペースがない
1783 | (defn some-fun
1784 | []
1785 | ;; FIXME:Replace baz with the newer bar.
1786 | (baz))
1787 | ----
1788 |
1789 | ==== アノテーションのインデント [[indent-annotations]]
1790 |
1791 | 詳細が複数行にわたる場合、2行目以降は1行目に合わせてインデントするべきだ。
1792 |
1793 | [source,clojure]
1794 | ----
1795 | ;; 良い
1796 | (defn some-fun
1797 | []
1798 | ;; FIXME: This has crashed occasionally since v1.2.3. It may
1799 | ;; be related to the BarBazUtil upgrade. (xz 13-1-31)
1800 | (baz))
1801 |
1802 | ;; 悪い
1803 | (defn some-fun
1804 | []
1805 | ;; FIXME: This has crashed occasionally since v1.2.3. It may
1806 | ;; be related to the BarBazUtil upgrade. (xz 13-1-31)
1807 | (baz))
1808 | ----
1809 |
1810 | ==== アノテーションにサインと日付を入れる [[sign-and-date-annotations]]
1811 |
1812 | アノテーションには記述者のイニシャルと日付を入れる。そうすればその妥当性を容易に示せる。
1813 |
1814 | [source,clojure]
1815 | ----
1816 | (defn some-fun
1817 | []
1818 | ;; FIXME: This has crashed occasionally since v1.2.3. It may
1819 | ;; be related to the BarBazUtil upgrade. (xz 13-1-31)
1820 | (baz))
1821 | ----
1822 |
1823 | ==== 例外的な行末アノテーション [[rare-eol-annotations]]
1824 |
1825 | ドキュメント化が不必要なほどに問題が明らかな箇所では、当該行の末尾に説明なしでアノテーションを付けても良い。この使用法は例外的であるべきで、規約ではない。
1826 |
1827 | [source,clojure]
1828 | ----
1829 | (defn bar
1830 | []
1831 | (sleep 100)) ; OPTIMIZE
1832 | ----
1833 |
1834 | ==== `TODO` [[todo]]
1835 |
1836 | 後日追加されるべき機能には `TODO` を使う。
1837 |
1838 | ==== `FIXME` [[fixme]]
1839 |
1840 | コードが壊れていて、修正の必要がある箇所には `FIXME` を使う。
1841 |
1842 | ==== `OPTIMIZE` [[optimize]]
1843 |
1844 | パフォーマンス問題の原因となりうる、遅かったり非効率なコードには `OPTIMIZE` を使う。
1845 |
1846 | ==== `HACK` [[hack]]
1847 |
1848 | 疑わしいコーディングの仕方がされており、リファクタリングすべき「コード・スメル」には `HACK` を用いる。
1849 |
1850 | ==== `REVIEW` [[review]]
1851 |
1852 | 意図するように動くかどうか確認すべき箇所には `REVIEW` を使う。例: `REVIEW: Are we sure this is how the client does X currently?`
1853 |
1854 | ==== カスタムアノテーション [[document-annotations]]
1855 |
1856 | そのほうが適切だと思えば、その他独自のアノテーションキーワードを用いる。ただし、プロジェクトの `README` などに忘れずにドキュメント化しておく。
1857 |
1858 | == ドキュメント [[documentation]]
1859 |
1860 | ドキュメント文字列は、Clojureコードにドキュメントを付加するための最も基本的な方法だ。多くの定義フォーム(例: `def`, `defn`, `defmacro`, `ns` )はドキュメント文字列をサポートしており、そのvarがパブリックであるかプライベートであるかに関わらず、基本的にはドキュメント文字列を活用するのが良い。
1861 |
1862 | 定義フォームがドキュメント文字列を直接的にサポートしていない場合でも、メタデータの `:doc` 属性にドキュメントを記述することができる。
1863 |
1864 | このセクションでは、Clojureコードのドキュメンテーションを行う上で、いくつかの慣用的方法とベストプラクティスを紹介する。
1865 |
1866 | === ドキュメント文字列が好ましい [[prefer-docstrings]]
1867 |
1868 | フォームがドキュメント文字列を直接的にサポートしている場合、 `:doc` メタデータよりもそれを用いるほうが良い。
1869 |
1870 | [source,clojure]
1871 | ----
1872 | ;; 良い
1873 | (defn foo
1874 | "This function doesn't do much."
1875 | []
1876 | ...)
1877 |
1878 | (ns foo.bar.core
1879 | "That's an awesome library.")
1880 |
1881 | ;; 悪い
1882 | (defn foo
1883 | ^{:doc "This function doesn't do much."}
1884 | []
1885 | ...)
1886 |
1887 | (ns ^{:doc "That's an awesome library.")
1888 | foo.bar.core)
1889 | ----
1890 |
1891 | === ドキュメント文字列の要約 [[docstring-summary]]
1892 |
1893 | ドキュメント文字列の最初の行は、大文字で始まる完結した文で、そのvarを簡潔に説明するものにする。これによって、ツール(ClojureエディタやIDE)が様々な場面でドキュメント文字列の要約を簡単に表示できるようになる。
1894 |
1895 | [source,clojure]
1896 | ----
1897 | ;; 良い
1898 | (defn frobnitz
1899 | "This function does a frobnitz.
1900 | It will do gnorwatz to achieve this, but only under certain
1901 | circumstances."
1902 | []
1903 | ...)
1904 |
1905 | ;; 悪い
1906 | (defn frobnitz
1907 | "This function does a frobnitz. It will do gnorwatz to
1908 | achieve this, but only under certain circumstances."
1909 | []
1910 | ...)
1911 | ----
1912 |
1913 | === ドキュメント文字列でのMarkdownの利用 [[markdown-docstrings]]
1914 |
1915 | https://github.com/cljdoc/cljdoc/blob/master/doc/userguide/for-library-authors.adoc#docstrings[cljdoc] などの有用なツールは、ドキュメント文字列内におけるMarkdownをサポートしているため、フォーマットをきれいに整えるのに利用すると良い。
1916 |
1917 | [source,clojure]
1918 | ----
1919 | ;; 良い
1920 | (defn qzuf-number
1921 | "Computes the [Qzuf number](https://wikipedia.org/qzuf) of the `coll`.
1922 | Supported options in `opts`:
1923 |
1924 | | key | description |
1925 | | --------------|-------------|
1926 | | `:finite-uni?`| Assume finite universe; default: `false`
1927 | | `:complex?` | If OK to return a [complex number](https://en.wikipedia.org/wiki/Complex_number); default: `false`
1928 | | `:timeout` | Throw an exception if the computation doesn't finish within `:timeout` milliseconds; default: `nil`
1929 |
1930 | Example:
1931 | ```clojure
1932 | (when (neg? (qzuf-number [1 2 3] {:finite-uni? true}))
1933 | (throw (RuntimeException. "Error in the Universe!")))
1934 | ```"
1935 | [coll opts]
1936 | ...)
1937 | ----
1938 |
1939 | === 引数のドキュメント化 [[document-pos-arguments]]
1940 |
1941 | 全ての引数をドキュメント化し、それらをバッククォート(`)で囲む。そうすることで、エディタやIDEが引数を識別できるようになり、より高度な機能を提供できる可能性がある。
1942 |
1943 | [source,clojure]
1944 | ----
1945 | ;; 良い
1946 | (defn watsitz
1947 | "Watsitz takes a `frob` and converts it to a znoot.
1948 | When the `frob` is negative, the znoot becomes angry."
1949 | [frob]
1950 | ...)
1951 |
1952 | ;; 悪い
1953 | (defn watsitz
1954 | "Watsitz takes a frob and converts it to a znoot.
1955 | When the frob is negative, the znoot becomes angry."
1956 | [frob]
1957 | ...)
1958 | ----
1959 |
1960 | === ドキュメントの参照 [[document-references]]
1961 |
1962 | ドキュメント文字列でのvarの参照を ` で囲み、ツールが識別できるようにする。リンクを張りたい場合は `[[..]]` で囲う。
1963 |
1964 | [source,clojure]
1965 | ----
1966 | ;; 良い
1967 | (defn wombat
1968 | "Acts much like `clojure.core/identity` except when it doesn't.
1969 | Takes `x` as an argument and returns that. If it feels like it.
1970 | See also [[kangaroo]]."
1971 | [x]
1972 | ...)
1973 |
1974 | ;; 悪い
1975 | (defn wombat
1976 | "Acts much like clojure.core/identity except when it doesn't.
1977 | Takes `x` as an argument and returns that. If it feels like it.
1978 | See also kangaroo."
1979 | [x]
1980 | ...)
1981 | ----
1982 |
1983 | === ドキュメント文字列の文法 [[docstring-grammar]]
1984 |
1985 | ドキュメント文字列は正しい英語の文で構成されるべきだ。全ての文は大文字で始まり、文法的に一貫していて、適切な句読点で終わる。また、各々の文の間には1つのスペースをはさむ。
1986 |
1987 | [source,clojure]
1988 | ----
1989 | ;; 良い
1990 | (def foo
1991 | "All sentences should end with a period (or maybe an exclamation mark).
1992 | The sentence should be followed by a space, unless it concludes the docstring.")
1993 |
1994 | ;; 悪い
1995 | (def foo
1996 | "all sentences should end with a period (or maybe an exclamation mark).
1997 | The sentence should be followed by a space, unless it concludes the docstring.")
1998 | ----
1999 |
2000 | === ドキュメント文字列のインデント [[docstring-indentation]]
2001 |
2002 | 複数行にわたるドキュメント文字列は、2つのスペースでインデントする。
2003 |
2004 | [source,clojure]
2005 | ----
2006 | ;; 良い
2007 | (ns my.ns
2008 | "It is actually possible to document a ns.
2009 | It's a nice place to describe the purpose of the namespace and maybe even
2010 | the overall conventions used. Note how _not_ indenting the docstring makes
2011 | it easier for tooling to display it correctly.")
2012 |
2013 | ;; 悪い
2014 | (ns my.ns
2015 | "It is actually possible to document a ns.
2016 | It's a nice place to describe the purpose of the namespace and maybe even
2017 | the overall conventions used. Note how _not_ indenting the docstring makes
2018 | it easier for tooling to display it correctly.")
2019 | ----
2020 |
2021 | === ドキュメント文字列の先頭・末尾の空白 [[docstring-leading-trailing-whitespace]]
2022 |
2023 | ドキュメント文字列の最初と最後には余計な空白を入れない。
2024 |
2025 | [source,clojure]
2026 | ----
2027 | ;; 良い
2028 | (def foo
2029 | "I'm so awesome."
2030 | 42)
2031 |
2032 | ;; 悪い
2033 | (def silly
2034 | " It's just silly to start a docstring with spaces.
2035 | Just as silly as it is to end it with a bunch of them. "
2036 | 42)
2037 | ----
2038 |
2039 | === 関数名の後ろのドキュメント文字列 [[docstring-after-fn-name]]
2040 |
2041 | ドキュメント文字列を付加するときは、上記フォームを用いる関数は特に、ドキュメント文字列は引数ベクタの後ろではなく、関数名の後ろに置くことに注意する。前者は文法的には間違っておらずエラーにもならないが、そのvarにドキュメントは付加されず、関数本体に1つのフォームとしてその文字列が含まれることになる。
2042 |
2043 | [source,clojure]
2044 | ----
2045 | ;; 良い
2046 | (defn foo
2047 | "docstring"
2048 | [x]
2049 | (bar x))
2050 |
2051 | ;; 悪い
2052 | (defn foo [x]
2053 | "docstring"
2054 | (bar x))
2055 | ----
2056 |
2057 | == 実際のコードでは [[existential]]
2058 |
2059 | === 関数型的に [[be-functional]]
2060 |
2061 | 関数型的にコードを書き、そのほうが適切なときのみミュータブルにする。
2062 |
2063 | === 一貫させる [[be-consistent]]
2064 |
2065 | 一貫させる。理想的には、このガイドの通りにする。
2066 |
2067 | === 常識的に [[common-sense]]
2068 |
2069 | 常識的に考える。
2070 |
2071 | == テスト [[testing]]
2072 |
2073 | === テストディレクトリの構造 [[test-directory-structure]]
2074 |
2075 | テストコードは `test/yourproject/` などの( `src/yourproject/` とは)別ディレクトリに配置する。ビルドツールは必要に応じてこれらのディレクトリを用意してくれる。ほとんどのテンプレートは自動的にこれらのディレクトリを生成する。
2076 |
2077 | === テストの名前空間 [[test-ns-naming]]
2078 |
2079 | 名前空間は `yourproject.something-test` のように命名し、ファイルは `test/yourproject/something_test.clj` (あるいは `.cljc`, `cljs` )に普通は作成する。
2080 |
2081 | === テストの命名規約 [[test-naming]]
2082 |
2083 | `clojure.test` を用いるときは、 `deftest` でテストを定義し、 `something-test` と名付ける。
2084 |
2085 | [source,clojure]
2086 | ----
2087 | ;; 良い
2088 | (deftest something-test ...)
2089 |
2090 | ;; 悪い
2091 | (deftest something-tests ...)
2092 | (deftest test-something ...)
2093 | (deftest something ...)
2094 | ----
2095 |
2096 | == ライブラリの構成 [[library-organization]]
2097 |
2098 | === ライブラリの識別子 [[lib-coordinates]]
2099 |
2100 | 他の人が使えるようにライブラリを公開する場合、 http://central.sonatype.org/pages/choosing-your-coordinates.html[Central Repositoryのガイドライン] にしたがって `groupId` と `artifactId` を選ぶ。これにより名前の衝突が避けられ、幅広い利用が促進される。 https://github.com/stuartsierra/component[Component] が良い例で、識別子は `com.stuartsierra/component` だ。
2101 |
2102 | 異なるアプローチとして、`groupId` にドメインではなくプロジェクト名(あるいは組織名)がよく用いられる。
2103 |
2104 | 例:
2105 |
2106 | * `cider/cider-nrepl`
2107 | * `nrepl/nrepl`
2108 | * `nrepl/drawbridge`
2109 | * `clj-commons/fs`
2110 |
2111 | === 依存の最小化 [[lib-min-dependencies]]
2112 |
2113 | 不必要な依存を避ける。たとえば、何百もの使う予定のないvarを含んだライブラリに依存するよりも、3行のユーティリティ関数をプロジェクトにコピーしてしまうほうが良い。
2114 |
2115 | === ツールの分離 [[lib-core-separate-from-tools]]
2116 |
2117 | コアの機能とインテグレーション部分は別々のアーティファクトにする。そうすれば、ユーザはあなたのライブラリを無関係なツール依存に制限されることなく利用できる。たとえば、 https://github.com/stuartsierra/component[Component] はコア機能を提供し、 https://github.com/stuartsierra/reloaded[reloaded] はLeiningenとのインテグレーションを提供している。
2118 |
2119 | == Lintツール [[lint-tools]]
2120 |
2121 | 慣用的なClojureコードを書くのを助けてくれるLintツールが、Clojureコミュニティによって作られている。
2122 |
2123 | * https://github.com/technomancy/slamhound[Slamhound] は既存のコードから適切な `ns` 定義を自動的に生成してくれる。
2124 | * https://github.com/jonase/kibit[kibit] はClojure向けの静的コード解析ツールだ。より慣用的な関数やマクロの探索には https://github.com/clojure/core.logic[core.logic] を用いている。
2125 | * https://github.com/borkdude/clj-kondo[clj-kondo] は、このスタイルガイドに基づいて、多くの非推奨パターンを発見し、改善案を提案してくれるLintツールだ。
2126 |
2127 | == 貢献 [[contributing]]
2128 |
2129 | このスタイルガイドはまだまだ書き換えることができます。Clojureのコーディングスタイルに関心のある皆さんと一緒に取り組み、最終的にはClojureコミュニティ全体にとって有益な情報源を作り上げたいと思っています。
2130 |
2131 | 遠慮なく改良案の https://github.com/bbatsov/clojure-style-guide/issues[Pull Requestを作って] ください。どうかよろしくお願いします。
2132 |
2133 | https://www.patreon.com/bbatsov[Patreon] あるいは https://www.paypal.me/bbatsov[PayPal] を通して、金銭的にこのスタイルガイド(およびCIDER、nREPL、orchardといった私のClojureプロジェクト)を支援することもできます。
2134 |
2135 | == 広めてください [[spread-the-word]]
2136 |
2137 | コミュニティドリブンのスタイルガイドは、その存在を知らないコミュニティではあまり役に立ちません。どうか、このガイドについてツイートをして、あなたの友達や同僚と共有してください。頂いたあらゆるコメントや提案、意見がほんの少しずつ、このガイドを形作っていくのです。みんなで最高のスタイルガイドを作りましょう。
2138 |
--------------------------------------------------------------------------------