# The Clojure Style Guide
## Примечание переводчика
Это перевод [оригинального руководства](https://github.com/bbatsov/clojure-style-guide),
составленного Божидаром Бацовым ([Bozhidar Batsov](https://github.com/bbatsov)).
У меня возникли некоторые сложности с переводом некоторых терминов, поэтому я
сразу привожу их список:
* forms - формы (например, special forms - специальные формы)
* body - тело (как в выражении "тело функции").
* binding - связывание, связка (например, local binding - локальное связывание,
two-way binding - двустороннее связывание, `let`-bindings - связки `let`)
* keywords - ключи (`:whatever`).
* hashmap, map - хеш (`{:key value}`)
* docstring - док. строка
* argument vector - параметры, список параметров (например, в определении
`(defn f [x y] ...)` argument vector - это `[x y]`). **Важно!** в контексте
определения функции я использую термин *параметр*, а в контексте вызова -
*аргумент*.
* multi-arity - многоарность, многоарная (функция)
* top-level - верхний уровень, верхнеуровневый
* forward reference - раннее определение
* transient - изменяемые. см. [документацию](https://clojure.org/reference/transients)
* будет дополняться
Также я иногда буду оставлять в скобках оригинал выражения/термина.
## Введение
> Role models are important.
> -- Officer Alex J. Murphy / RoboCop
Данное руководство предлагает наилучшие рекомендации по оформлению Clojure кода
для того, чтобы Clojure-программисты могли писать код, который смогут
поддерживать другие Clojure-программисты. Руководство по оформлению кода,
отражающее практики, используемые в реальном мире, будет использоваться людьми,
а руководство, придерживающееся отвергнутых людьми, которым он должен помочь,
рискует не быть используемым ими вовсе (не важно, насколько он при этом хорош).
Руководство поделено на несколько разделов, объединяющих связанные между собой
правила. Я постарался добавить пояснения к правилам (если же они опущены,
значит, я предположил, что они и так очевидны и в пояснениях не нуждаются).
Я не взял все эти идеи с потолка. Они основаны по большей части на моем личном
обширном опыте разработки, комментариях и предложениях членов Clojure сообщества
и некоторых авторитетных источников информации по Clojure, например,
["Clojure Programming"](http://www.clojurebook.com/) и
["The Joy of Clojure"](http://joyofclojure.com/).
Руководство все еще разрабатывается: некоторые разделы отсутствуют, некоторые
все еще не завершены, каким-то правилам не хватает хороших
примеров. Когда-нибудь эти недостатки будут устранены, а пока просто держите
их в уме.
Обратите внимание, что разработчики Clojure также поддерживают список
[стандартов по разработке библиотек](http://dev.clojure.org/display/community/Library+Coding+Standards)
Вы можете сгенерировать PDF или HTML копию данного руководства с помощью
[Pandoc](http://pandoc.org/).
Переводы доступны на следующих языках:
* [Оригинал на английском](https://github.com/bbatsov/clojure-style-guide)
* [Китайский](https://github.com/geekerzp/clojure-style-guide/blob/master/README-zhCN.md)
* [Японский](https://github.com/totakke/clojure-style-guide/blob/ja/README.md)
* [Корейский](https://github.com/kwakbab/clojure-style-guide/blob/master/README-koKO.md)
* [Португальский](https://github.com/theSkilled/clojure-style-guide/blob/pt-BR/README.md)
(дорабатывается).
* Русский - вы здесь.
## Содержание
* [Организация кода](#source-code-layout--organization)
* [Синтаксис](#syntax)
* [Именование (переменных, функций и т.д.)](#naming)
* [Коллекции](#collections)
* [Изменение состояния](#mutation) (ориг. Mutation)
* [Строки](#strings)
* [Исключения](#exceptions)
* [Макросы](#macros)
* [Комментарии](#comments)
* [Аннотации](#comment-annotations) (ориг. Comment Annotations)
* [Документирование](#documentation)
* [Общие рекомендации](#existential) (ориг. Existential)
* [Инструменты](#tooling)
* [Тестирование](#testing)
* [Создание библиотек](#library-organization)
## Организация кода
> Почти каждый уверен, что все стили кода, кроме его собственного, - уродливые и
> нечитаемые. Если убрать "кроме его собственного", то, возможно, они
> правы...
> -- Jerry Coffin (об отступах)
*
Используйте **пробелы** для отступов. Никаких табов.
[[link](#spaces)]
*
Используйте 2 пробела для тела формы. Формы с телом включают `def`-подобные
конструкции, специальные формы и макросы, вводящие локальное связывание
(например, `loop`, `let`, `when-let`) и многие макросы вроде `when`, `cond`,
`as->`, `cond->`, `case`, `with-*` и т.д.
[[link](#body-indentation)]
```Clojure
;; хорошо
(when something
(something-else))
(with-out-str
(println "Hello, ")
(println "world!"))
;; плохо - четыре пробела
(when something
(something-else))
;; плохо - один пробел
(with-out-str
(println "Hello, ")
(println "world!"))
```
*
Выравнивайте по вертикали аргументы функции (макроса), если они расположены на
нескольких строках.
[[link](#vertically-align-fn-args)]
```Clojure
;; хорошо
(filter even?
(range 1 10))
;; плохо
(filter even?
(range 1 10))
```
*
Используйте отступ в один пробел для аргументов функции (макроса), если на одной
строке с именем функции их (аргументов) нет.
[[link](#one-space-indent)]
```Clojure
;; хорошо
(filter
even?
(range 1 10))
(or
ala
bala
portokala)
;; плохо - два пробела
(filter
even?
(range 1 10))
(or
ala
bala
portokala)
```
*
Выравнивайте по вертикали `let`-связки (переменные) и ключи для хешей
[[link](#vertically-align-let-and-map)]
```Clojure
;; хорошо
(let [thing1 "some stuff"
thing2 "other stuff"]
{:thing1 thing1
:thing2 thing2})
;; плохо
(let [thing1 "some stuff"
thing2 "other stuff"]
{:thing1 thing1
:thing2 thing2})
```
*
Можете не использовать перенос строки между именем функции и ее параметрами
в `defn`, если отсутствует док. строка.
[[link](#optional-new-line-after-fn-name)]
```Clojure
;; хорошо
(defn foo
[x]
(bar x))
;; хорошо
(defn foo [x]
(bar x))
;; плохо
(defn foo
[x] (bar x))
```
*
Размещайте `dispatch-val`
(см. [`defmethod`](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/defmethod))
на той же строке, что и имя функции.
[[link](#multimethod-dispatch-val-placement)]
```Clojure
;; хорошо
(defmethod foo :bar [x] (baz x))
(defmethod foo :bar
[x]
(baz x))
;; плохо
(defmethod foo
:bar
[x]
(baz x))
(defmethod foo
:bar [x]
(baz x))
```
*
Можете опустить перенос строки между списком параметров и коротким телом функции
[[link](#oneline-short-fn)]
```Clojure
;; хорошо
(defn foo [x]
(bar x))
;; хорошо для короткого тела функции
(defn foo [x] (bar x))
;; хорошо для многоарных функций
(defn foo
([x] (bar x))
([x y]
(if (predicate? x)
(bar x)
(baz x))))
;; плохо
(defn foo
[x] (if (predicate? x)
(bar x)
(baz x)))
```
*
Для каждой арности функции расставляйте отступы в соответствии с параметрами.
[[link](#multiple-arity-indentation)]
```Clojure
;; хорошо
(defn foo
"I have two arities."
([x]
(foo x 1))
([x y]
(+ x y)))
;; плохо - слишком большой отступ
(defn foo
"I have two arities."
([x]
(foo x 1))
([x y]
(+ x y)))
```
*
Упорядочивайте определения арностей функции от наименьшего числа параметров к
наибольшему. Частый случай многоарных функций - когда K параметров полностью
определяют поведение функции, меньшая арность использует K-арность и большие
(>K) арности предоставляют возможность передать функции переменное количество
аргументов (например, с помощью `&`).
[[link](#multiple-arity-order)]
```Clojure
;; хорошо - легко найти n-ую арность
(defn foo
"I have two arities."
([x]
(foo x 1))
([x y]
(+ x y)))
;; сойдет - остальные арности используют двуарный вариант
(defn foo
"I have two arities."
([x y]
(+ x y))
([x]
(foo x 1))
([x y z & more]
(reduce foo (foo x (foo y z)) more)))
;; плохо - нет какого-либо осмысленного порядка
(defn foo
([x] 1)
([x y z] (foo x (foo y z)))
([x y] (+ x y))
([w x y z & more] (reduce foo (foo w (foo x (foo y z))) more)))
```
*
Используйте Unix окончания строк. (Пользователи *BSD/Solaris/Linux/OSX
используют их по-умолчанию, а пользователям Windows нужно проявлять
осторожность).
[[link](#crlf)]
* Если вы используете Git, то можете добавить следующую настройку в
конфигурацию своего проекта, чтобы уберечь себя от Windows-переносов:
```
bash$ git config --global core.autocrlf true
```
*
Если какой-либо текст предшествует открытой скобке (`(`, `{`, `[`)
или следует за закрытой скобкой, то отделяйте его от скобки пробелом.
И наоборот, не добавляйте пробелов между открытой скобки и последующим текстов
и между текстом и последующей скобкой
[[link](#bracket-spacing)]
```Clojure
;; хорошо
(foo (bar baz) quux)
;; плохо
(foo(bar baz)quux)
(foo ( bar baz ) quux)
```
> Синтаксический сахар вызывает рак точек с запятой (semicolon cancer).
> -- Alan Perlis
*
Не используйте запятые между элементами последовательностей (списки, векторы)
[[link](#no-commas-for-seq-literals)]
```Clojure
;; хорошо
[1 2 3]
(1 2 3)
;; плохо
[1, 2, 3]
(1, 2, 3)
```
*
Подумайте об улучшении читаемости хеш-литералов с помощью использования
запятых и переносов.
[[link](#opt-commas-in-map-literals)]
```Clojure
;; хорошо
{:name "Bruce Wayne" :alter-ego "Batman"}
;; хорошо и в некотором роде более читаемо
{:name "Bruce Wayne"
:alter-ego "Batman"}
;; хорошо и при этом более компактно (чем предыдущий пример)
{:name "Bruce Wayne", :alter-ego "Batman"}
```
*
Оставляйте конечные скобки на одной строке
[[link](#gather-trailing-parens)]
```Clojure
;; хорошо - на одной строке
(when something
(something-else))
;; плохо - на разных строках
(when something
(something-else)
)
```
*
Оставляйте пустые строки между верхнеуровневыми формами
[[link](#empty-lines-between-top-level-forms)]
```Clojure
;; хорошо
(def x ...)
(defn foo ...)
;; плохо
(def x ...)
(defn foo ...)
```
Исключение из правил - группировка нескольких связанных `def`
```Clojure
;; хорошо
(def min-rows 10)
(def max-rows 20)
(def min-cols 15)
(def max-cols 30)
```
*
Не оставляйте пустые строки посреди определения функции или
макроса. Исключением может быть группировки парных конструкций, например, в
`let` и `cond`.
[[link](#no-blank-lines-within-def-forms)]
*
Старайтесь избегать написания строк длиннее 80 символов
[[link](#80-character-limits)]
*
Избегайте лишних пробелов в конце (строк)
[[link](#no-trailing-whitespace)]
*
Для каждого пространства имен создавайте отдельный файл
[[link](#one-file-per-namespace)]
*
Определяйте каждое пространство имен с помощью `ns` с использованием `:refer`,
`:require` и `:import`. Желательно, в таком порядке.
[[link](#comprehensive-ns-declaration)]
```Clojure
(ns examples.ns
(:refer-clojure :exclude [next replace remove])
(:require [clojure.string :as s :refer [blank?]]
[clojure.set :as set]
[clojure.java.shell :as sh])
(:import java.util.Date
java.text.SimpleDateFormat
[java.util.concurrent Executors
LinkedBlockingQueue]))
```
*
В `ns` предпочитайте `:require :as` вместо `:require :refer` и, тем более,
`:require :refer :all`. Также предпочитайте `:require` вместо `:use`.
Последнее должно считаться устаревшим в новом коде.
[[link](#prefer-require-over-use)]
```Clojure
;; хорошо
(ns examples.ns
(:require [clojure.zip :as zip]))
;; хорошо
(ns examples.ns
(:require [clojure.zip :refer [lefts rights]]))
;; приемлемо
(ns examples.ns
(:require [clojure.zip :refer :all]))
;; плохо
(ns examples.ns
(:use clojure.zip))
```
*
Старайтесь не использовать односоставные пространства имен.
[[link](#no-single-segment-namespaces)]
```Clojure
;; хорошо
(ns example.ns)
;; плохо
(ns example)
```
*
Избегайте не использовать слишком длинные названия пространства имен
(т.е. состоящие более чем из 5 частей).
[[link](#namespaces-with-5-segments-max)]
*
Избегайте функций, состоящих из более 10 строк. В идеале, большинство функций
должны быть менее 5 строк длиной.
[[link](#10-loc-per-fn-limit)]
*
Избегайте создания функций с более чем тремя-четырьмя позиционными параметрами.
(прим. переводчика: `[x y & more]` - это всего три параметра, хотя аргументов
можно передать гораздо больше)
[[link](#4-positional-fn-params-limit)]
*
Не используйте ранние определения (forward references). Иногда они необходимы,
то это довольно редкий случай на практике.
[[link](#forward-references)]
## Syntax
*
Избегайте использования функций, влияющих на пространство имен, например,
`require` и `refer`. Они совершенно не нужны, если вы не находитесь в REPL.
[[link](#ns-fns-only-in-repl)]
*
Используйте `declare`, когда вам *необходимы* ранние определения.
[[link](#declare)]
*
Старайтесь использовать функции высшего порядка (такие как `map`) вместо `loop/recur`.
[[link](#higher-order-fns)]
*
Старайтесь использовать пред- и пост- условия функции вместо проверок внутри
тела функции
[[link](#pre-post-conditions)]
```Clojure
;; хорошо
(defn foo [x]
{:pre [(pos? x)]}
(bar x))
;; плохо
(defn foo [x]
(if (pos? x)
(bar x)
(throw (IllegalArgumentException. "x must be a positive number!")))
```
*
Don't define vars inside functions.
[[link](#dont-def-vars-inside-fns)]
```Clojure
;; очень плохо
(defn foo []
(def x 5)
...)
```
*
Не перекрывайте имена из `clojure.core` локальными именами.
[[link](#dont-shadow-clojure-core)]
```Clojure
;; плохо - если вам понадобится функция map, вам придется использовать ее
;; с помощью clojure.core/map
(defn foo [map]
...)
```
*
Используйте `alter-var-root` вместо `def` для изменения значения переменной.
[[link]](#alter-var)
```Clojure
;; хорошо
(def thing 1)
; какие-то действия над thing
(alter-var-root #'thing (constantly nil)) ; значение thing теперь nil
;; плохо
(def thing 1)
; какие-то действия над thing
(def thing nil) ; значение thing теперь nil
```
*
Используйте `seq`, когда нужно проверить, что последовательность пуста (этот
прием иногда называется *nil punning*).
[[link](#nil-punning)]
```Clojure
;; хорошо
(defn print-seq [s]
(when (seq s)
(prn (first s))
(recur (rest s))))
;; плохо
(defn print-seq [s]
(when-not (empty? s)
(prn (first s))
(recur (rest s))))
```
*
Предпочитайте `vec` вместо `into`, когда вам нужно превратить
последовательность в вектор.
[[link](#to-vector)]
```Clojure
;; хорошо
(vec some-seq)
;; плохо
(into [] some-seq)
```
*
Используйте `when` вместо `(if ... (do ...)`.
[[link](#when-instead-of-single-branch-if)]
```Clojure
;; хорошо
(when pred
(foo)
(bar))
;; плохо
(if pred
(do
(foo)
(bar)))
```
*
Используйте `if-let` вместо `let` + `if`.
[[link](#if-let)]
```Clojure
;; хорошо
(if-let [result (foo x)]
(something-with result)
(something-else))
;; плохо
(let [result (foo x)]
(if result
(something-with result)
(something-else)))
```
*
Используйте `when-let` вместо `let` + `when`.
[[link](#when-let)]
```Clojure
;; хорошо
(when-let [result (foo x)]
(do-something-with result)
(do-something-more-with result))
;; плохо
(let [result (foo x)]
(when result
(do-something-with result)
(do-something-more-with result)))
```
*
Используйте `if-not` вместо `(if (not ...) ...)`.
[[link](#if-not)]
```Clojure
;; хорошо
(if-not pred
(foo))
;; плохо
(if (not pred)
(foo))
```
*
Используйте `when-not` вместо `(when (not ...) ...)`.
[[link](#when-not)]
```Clojure
;; хорошо
(when-not pred
(foo)
(bar))
;; плохо
(when (not pred)
(foo)
(bar))
```
*
Используйте `when-not` вместо `(if-not ... (do ...)`.
[[link](#when-not-instead-of-single-branch-if-not)]
```Clojure
;; хорошо
(when-not pred
(foo)
(bar))
;; плохо
(if-not pred
(do
(foo)
(bar)))
```
*
Используйте `not=` Вместо `(not (= ...))`.
[[link](#not-equal)]
```Clojure
;; хорошо
(not= foo bar)
;; плохо
(not (= foo bar))
```
*
Используйте `printf` вместо `(print (format ...))`.
[[link](#printf)]
```Clojure
;; хорошо
(printf "Hello, %s!\n" name)
;; сойдет
(println (format "Hello, %s!" name))
```
*
При сравнениях не забывайте, что функции `<`, `>` и т.д. принимают переменное
количество аргументов.
[[link](#multiple-arity-of-gt-and-ls-fns)]
```Clojure
;; хорошо
(< 5 x 10)
;; плохо
(and (> x 5) (< x 10))
```
*
Используйте `%` вместо `%1` в литералах функции с одним параметром
[[link](#single-param-fn-literal)]
```Clojure
;; хорошо
#(Math/round %)
;; плохо
#(Math/round %1)
```
*
Используйте `%1` вместо `%` в литералах функций с несколькими параметрами.
[[link](#multiple-params-fn-literal)]
```Clojure
;; хорошо
#(Math/pow %1 %2)
;; плохо
#(Math/pow % %2)
```
*
Не оборачивайте функции в анонимные функции, если это не требуется.
[[link](#no-useless-anonymous-fns)]
```Clojure
;; хорошо
(filter even? (range 1 10))
;; плохо
(filter #(even? %) (range 1 10))
```
*
Не используйте литералы функций, если тело функции будет состоять из более чем
одной формы.
[[link](#no-multiple-forms-fn-literals)]
```Clojure
;; хорошо
(fn [x]
(println x)
(* x 2))
;; плохо (you need an explicit do form)
#(do (println %)
(* % 2))
```
*
Предпочитайте `complement` вместо использования анонимной функции.
[[link](#complement)]
```Clojure
;; хорошо
(filter (complement some-pred?) coll)
;; плохо
(filter #(not (some-pred? %)) coll)
```
Это правило, очевидно, должно быть проигнорированно, если дополнение функции
уже существует в форме другой функции (например, `even?` и `odd?`).
*
Используйте `comp`, когда это упрощает код.
[[link](#comp)]
```Clojure
;; Предполагается `(:require [clojure.string :as str])`...
;; хорошо
(map #(str/capitalize (str/trim %)) ["top " " test "])
;; лучше
(map (comp str/capitalize str/trim) ["top " " test "])
```
*
Используйте `partial`, когда это упрощает код.
[[link](#partial)]
```Clojure
;; хорошо
(map #(+ 5 %) (range 1 10))
;; (кажется) лучше
(map (partial + 5) (range 1 10))
```
*
Используйте threading макрос `->` (thread-first) and `->>` (thread-last) в
глубоко вложенных формах.
[[link](#- `` - hreading-macros)]
```Clojure
;; хорошо
(-> [1 2 3]
reverse
(conj 4)
prn)
;; не так хорошо
(prn (conj (reverse [1 2 3])
4))
;; хорошо
(->> (range 1 10)
(filter even?)
(map (partial * 2)))
;; не так хорошо
(map (partial * 2)
(filter even? (range 1 10)))
```
*
Используйте `:else`, когда нужно перехватить значение, не проходящее остальные
условия.
[[link](#else-keyword-in-cond)]
```Clojure
;; хорошо
(cond
(neg? n) "negative"
(pos? n) "positive"
:else "zero")
;; плохо
(cond
(neg? n) "negative"
(pos? n) "positive"
true "zero")
```
*
Предпочитайте `condp` вместо `cond`, когда предикатное выражение не меняется.
[[link](#condp)]
```Clojure
;; хорошо
(cond
(= x 10) :ten
(= x 20) :twenty
(= x 30) :thirty
:else :dunno)
;; намного лучше
(condp = x
10 :ten
20 :twenty
30 :thirty
:dunno)
```
*
Предпочитайте `case` вместо `cond` и `condp`, когда условные выражения -
константы, известные еще на этапе компиляции.
[[link](#case)]
```Clojure
;; хорошо
(cond
(= x 10) :ten
(= x 20) :twenty
(= x 30) :forty
:else :dunno)
;; лучше
(condp = x
10 :ten
20 :twenty
30 :forty
:dunno)
;; самый лучший
(case x
10 :ten
20 :twenty
30 :forty
:dunno)
```
*
Используйте короткие формы в `cond` и подобных. Если это невозможно, то
визуально разделите формы на пары с помощью комментариев и пустых строк.
[[link](#shor-forms-in-cond)]
```Clojure
;; хорошо
(cond
(test1) (action1)
(test2) (action2)
:else (default-action))
;; наверное, сойдет
(cond
;; test case 1
(test1)
(long-function-name-which-requires-a-new-line
(complicated-sub-form
(-> 'which-spans multiple-lines)))
;; test case 2
(test2)
(another-very-long-function-name
(yet-another-sub-form
(-> 'which-spans multiple-lines)))
:else
(the-fall-through-default-case
(which-also-spans 'multiple
'lines)))
```
*
Используйте `set` как предикат, если возможно.
[[link](#set-as-predicate)]
```Clojure
;; хорошо
(remove #{1} [0 1 2 3 4 5])
;; плохо
(remove #(= % 1) [0 1 2 3 4 5])
;; хорошо
(count (filter #{\a \e \i \o \u} "mary had a little lamb"))
;; плохо
(count (filter #(or (= % \a)
(= % \e)
(= % \i)
(= % \o)
(= % \u))
"mary had a little lamb"))
```
*
Используйте `(inc x)` и `(dec x)` вместо `(+ x 1)` и `(- x 1)`.
[[link](#inc-and-dec)]
*
Используйте `(pos? x)`, `(neg? x)` и `(zero? x)` вместо `(> x 0)`,
`(< x 0)` и `(= x 0)`.
[[link](#pos-and-neg)]
*
Используйте `list*` вместо последовательных вызовов `cons`.
[[link](#list-star-instead-of-nested-cons)]
```Clojure
;; хорошо
(list* 1 2 3 [4 5])
;; плохо
(cons 1 (cons 2 (cons 3 [4 5])))
```
*
Используйте синт. сахар для работы с Java.
[[link](#sugared-java-interop)]
```Clojure
;;; создание объекта
;; хорошо
(java.util.ArrayList. 100)
;; плохо
(new java.util.ArrayList 100)
;;; вызов статического метода
;; хорошо
(Math/pow 2 10)
;; плохо
(. Math pow 2 10)
;;; вызов метода объекта
;; хорошо
(.substring "hello" 1 3)
;; плохо
(. "hello" substring 1 3)
;;; статические поля
;; хорошо
Integer/MAX_VALUE
;; плохо
(. Integer MAX_VALUE)
;;; поля объекта
;; хорошо
(.someField some-object)
;; плохо
(. some-object someField)
```
*
Используйте краткий синтаксис для описания метаданных, если они содержат
только ключи и только значения `true`.
[[link](#compact-metadata-notation-for-true-flags)]
```Clojure
;; хорошо
(def ^:private a 5)
;; плохо
(def ^{:private true} a 5)
```
*
Помечайте приватные части кода.
[[link](#private)]
```Clojure
;; хорошо
(defn- private-fun [] ...)
(def ^:private private-var ...)
;; плохо
(defn private-fun [] ...) ; не приватная
(defn ^:private private-fun [] ...) ; слишком многословно
(def private-var ...) ; не приватная
```
*
Для доступа к приватной переменной (например, в рамках тестирования)
используйте форму `@#'some.ns/var`.
[[link](#access-private-var)]
*
Будьте внимательны с тем, к чему конкретно вы привязываете метаданные.
[[link](#attach-metadata-carefully)]
```Clojure
;; мы привязываем метаданные к переменной `a`
(def ^:private a {})
(meta a) ;=> nil
(meta #'a) ;=> {:private true}
;; мы привязываем метаданные к хешу
(def a ^:private {})
(meta a) ;=> {:private true}
(meta #'a) ;=> nil
```
## Именование
> Единственными сложностями программирования являются проверка актуальности
> кеша и именование.
> -- Phil Karlton
*
Когда именуете пространство имен, предпочитайте следующие шаблоны:
[[link](#ns-naming-schemas)]
* `проект.модуль`
* `компания.проект.модуль`
*
Используйте `lisp-case` в отдельных частях пространства имен (например, `bruce.project-euler`)
[[link](#lisp-case-ns)]
*
Используйте `lisp-case` для имен функций и переменных.
[[link](#lisp-case)]
```Clojure
;; хорошо
(def some-var ...)
(defn some-fun ...)
;; плохо
(def someVar ...)
(defn somefun ...)
(def some_fun ...)
```
*
Используйте `CamelCase` для протоколов, записей, структур и типов. Сохраняйте
акронимы вроде HTTP, RFC, XML в верхнем регистре.
[[link](#CamelCase-for-protocols-records-structs-and-types)]
*
Имена предикатов (функций, возвращающих булевое значение) должны оканчиваться
вопросительным знаком (например, `even?`).
[[link](#pred-with-question-mark)]
```Clojure
;; хорошо
(defn palindrome? ...)
;; плохо
(defn palindrome-p ...) ; стиль Common Lisp
(defn is-palindrome ...) ; стиль Java
```
*
Имена функций/макросов, которые небезопасны в транзакциях
[STM](http://en.wikipedia.org/wiki/Software_transactional_memory)
должны оканчиваться восклицательным знаком (например, `reset!`)
[[link](#changing-state-fns-with-exclamation-mark)]
*
Используйте `->` вместо `to` в названиях приводящих (к типу) функций.
[[link](#arrow-instead-of-to)]
```Clojure
;; хорошо
(defn f->c ...)
;; не так хорошо
(defn f-to-c ...)
```
*
Используйте `*звездочки*` (в ориг. earmuffs) для того, что планируется
переопределить.
[[link](#earmuffs-for-dynamic-vars)]
```Clojure
;; хорошо
(def ^:dynamic *a* 10)
;; плохо
(def ^:dynamic a 10)
```
*
Не используйте никакой особенной записи для констант - предполагается, что все
есть константы, если не указано обратное.
[[link](#dont-flag-constants)]
*
Используйте `_` при именовании параметров (в т.ч. при деструктурировании),
которые не будут использоваться в коде.
[[link](#underscore-for-unused-bindings)]
```Clojure
;; хорошо
(let [[a b _ c] [1 2 3 4]]
(println a b c))
(dotimes [_ 3]
(println "Hello!"))
;; плохо
(let [[a b c d] [1 2 3 4]]
(println a b d))
(dotimes [i 3]
(println "Hello!"))
```
*
Следуйте примеру `clojure.core` и используйте типичные имена вроде `pred` и
`coll`.
[[link](#idiomatic-names)]
* в функциях:
* `f`, `g`, `h` - для функций
* `n` - целочисленный параметр (обычно размер)
* `index`, `i` - целочисленный индекс
* `x`, `y` - числа
* `xs` - последовательность
* `m` - хеш
* `s` - строка
* `re` - регулярное выражение
* `coll` - коллекция
* `pred` - предикат
* `& more` - при переменном числе аргументов
* `xf` - xform, transducer
* в макросах:
* `expr` - выражение
* `body` - тело макроса
* `binding` - связки макроса (список параметров)
## Коллекции
> Лучше иметь 100 функций, работающих с одной структурой данных
> нежели 10 функций, работающих с 10 структурами
> -- Alan J. Perlis
*
Избегайте использования списков для хранения данных (разве что список - это
именно то, что вам нужно)
[[link](#avoid-lists)]
*
Старайтесь использовать ключевые слова (ключи, keywords) в качестве ключей
хеша.
[[link](#keywords-for-hash-keys)]
```Clojure
;; хорошо
{:name "Bruce" :age 30}
;; плохо
{"name" "Bruce" "age" 30}
```
*
Старайтесь использовать литералы коллекций, если возможно. Однако для множеств
используйте литералы только в том случае, когда значениями являются константы,
известные на этапе компиляции.
[[link](#literal-col-syntax)]
```Clojure
;; хорошо
[1 2 3]
#{1 2 3}
(hash-set (func1) (func2)) ; значения определяются во время выполнения
;; плохо
(vector 1 2 3)
(hash-set 1 2 3)
#{(func1) (func2)} ; во время выполнения выбросит исключение, если (func1) = (func2)
```
*
Старайтесь не обращаться к членам коллекций по индексу, если возможно.
[[link](#avoid-index-based-coll-access)]
*
Старайтесь использовать ключи как функции для доступа к значениям хешей, если
возможно.
[[link](#keywords-as-fn-to-get-map-values)]
```Clojure
(def m {:name "Bruce" :age 30})
;; хорошо
(:name m)
;; слишком многословно
(get m :name)
;; плохо - возможна ошибка NullPointerException
(m :name)
```
*
Используйте факт, что большинство коллекций являются функциями.
[[link](#colls-as-fns)]
```Clojure
;; хорошо
(filter #{\a \e \o \i \u} "this is a test")
;; плохо - слишком страшно, чтобы показывать
```
*
Используйте факт, что ключи могут быть использованы как функции над
коллекцией.
[[link](#keywords-as-fns)]
```Clojure
((juxt :a :b) {:a "ala" :b "bala"})
```
*
Не используйте изменяемые (transient) коллекции, если только это
не критическая часть кода, требующая максимального быстродействия.
[[link](#avoid-transient-colls)]
*
Избегайте использования коллекций из Java.
[[link](#avoid-java-colls)]
*
Избегайте использования Java массивов. Разве что для случаев интеграции и
критического кода, много работающего с примитивами.
[[link](#avoid-java-arrays)]
## Изменение состояния
### Транзакционные ссылки (Refs)
*
Подумайте над оборачиванием всех IO вызовов макросом `io!`, чтобы избежать
гадких сюрпризов, если вы случайно запустите этот код внутри транзакции
[[link](#refs-io-macro)]
*
Избегайте использования `ref-set`, если возможно.
[[link](#refs-avoid-ref-set)]
```Clojure
(def r (ref 0))
;; хорошо
(dosync (alter r + 5))
;; плохо
(dosync (ref-set r 5))
```
*
Старайтесь держать размер транзакций (количество работы, инкапсулированное в них) настолько маленьким насколько возможно.
[[link](#refs-small-transactions)]
*
Избегайте наличия коротких и длительных транзакций, работающих с одной и той
же ссылкой.
[[link](#refs-avoid-short-long-transactions-with-same-ref)]
### Агенты
*
Используйте `send` только для действий, привязанных к процессору и не
блокирующих IO или другие потоки.
[[link](#agents-send)]
*
Используйте `send-off` для действий, которые могут заблокировать, "усыпить"
(sleep) или как-то иначе подвесить поток.
[[link](#agents-send-off)]
### Атомы
*
Избегайте обновления атомов внутри STM транзакции.
[[link](#atoms-no-update-within-transactions)]
*
Старайтесь использовать `swap!` вместо `reset!`, где возможно.
[[link](#atoms-prefer-swap-over-reset)]
```Clojure
(def a (atom 0))
;; хорошо
(swap! a + 5)
;; Не так хорошо
(reset! a 5)
```
## Строки
*
Старайтесь использовать для работы со строками функции из `clojure.string`
вместо интеграции с Java или введения собственных.
[[link](#prefer-clojure-string-over-interop)]
```Clojure
;; хорошо
(clojure.string/upper-case "bruce")
;; плохо
(.toUpperCase "bruce")
```
## Исключения
*
Используйте существующие исключения. Хороший Clojure код выбрасывает
исключения стандартных типов (если выбрасывает).
Например, `java.lang.IllegalArgumentException`,
`java.lang.UnsupportedOperationException`,
`java.lang.IllegalStateException`, `java.io.IOException`.
[[link](#reuse-existing-exception-types)]
*
Используйте `with-open` вместо `finally`.
[[link](#prefer-with-open-over-finally)]
## Макросы
*
Не пишите макросы, если хороша и функция.
[[link](#dont-write-macro-if-fn-will-do)]
*
Перед макросом напишите пример его использования.
[[link](#write-macro-usage-before-writing-the-macro)]
*
Разбивайте сложные макросы на меньшие функции.
[[link](#break-complicated-macros)]
*
Макрос обычно должен быть всего-лишь синтаксическим сахаром над обычной
функций. В таком случае вам будет легче комбинировать функции.
[[link](#macros-as-syntactic-sugar)]
*
Старайтесь использовать syntax-quote (\`) вместо того, чтобы создавать список
"вручную".
[[link](#syntax-quoted-forms)]
## Комментарии
> Хороший код является лучшей документацией для самого себя. Если вы собираетесь
> добавить комментарий, то спросите себя: "Как я могу улучшить свой код, чтобы
> этот комментарий был не нужен?". Улучшите код и задокументируйте его, чтобы он
> стал еще более чистым
> -- Steve McConnell
*
Старайтесь писать самодокументируемый код.
[[link](#self-documenting-code)]
*
Пишите заглавный комментарий минимум с четырьмя точками с запятой (`;;;;`).
[[link](#four-semicolons-for-heading-comments)]
*
Пишите комментарии верхнего уровня с тремя точками с запятой (`;;;`).
[[link](#three-semicolons-for-top-level-comments)]
*
Пишите комментарии к фрагменту кода перед ним и выравнивайте их, используя две
точки с запятой (`;;`).
[[link](#two-semicolons-for-code-fragment)]
*
Пишите боковые комментарии, начиная с одной точки с запятой (`;`).
[[link](#one-semicolon-for-margin-comments)]
*
Всегда оставляйте как минимум один пробел после точек с запятой.
[[link](#semicolon-space)]
```Clojure
;;;; Frob Grovel
;;; This section of code has some important implications:
;;; 1. Foo.
;;; 2. Bar.
;;; 3. Baz.
(defn fnord [zarquon]
;; If zob, then veeblefitz.
(quux zot
mumble ; Zibblefrotz.
frotz))
```
*
Комментарии, состоящие из более чем одного слова, начинаются с большой буквы и
используют пунктуацию. Отделяйте предложения
[одним пробелом](http://en.wikipedia.org/wiki/Sentence_spacing).
[[link](#english-syntax)]
*
Не оставляйте очевидных комментариев
[[link](#no-superfluous-comments)]
```Clojure
;; плохо
(inc counter) ; увеличивает counter на единицу
```
*
Комментарии должны быть актуальными. Устаревший комментарий хуже, чем
отсутствие комментария вообще.
[[link](#comment-upkeep)]
*
Используйте макрос `#_` вместо обычного комментирования, когда вам
нужно закомментировать форму.
[[link](#dash-underscore-reader-macro)]
```Clojure
;; хорошо
(+ foo #_(bar x) delta)
;; плохо
(+ foo
;; (bar x)
delta)
```
> Хороший код, как и хорошая шутка, не требует объяснения.
> -- Russ Olsen
*
Старайтесь не писать коммментарии, чтобы объяснить плохой код. Измените код,
чтобы он стал понятным ("Делай или не делай, не надо пытаться." --Йода).
[[link](#refactor-dont-comment)]
### Аннотации (Comment Annotations)
*
Аннотации обычно должны размещаться непосредственно над связанным кодом.
[[link](#annotate-above)]
*
Между ключевым словом аннотации и описанием должны стоять двоеточие и
пробел. Например: `TODO: add something cool`.
[[link](#annotate-keywords)]
*
Если описание занимает несколько строк, то каждая строка должна быть выровнена
в соответствии с первой (т.е. после ключевого слова).
[[link](#indent-annotations)]
*
Помечайте аннотацию своими инициалами и датой, чтобы актуальность было легче
отследить.
[[link](#sing-and-date-annotations)]
```Clojure
(defn some-fun
[]
;; FIXME: This has crashed occasionally since v1.2.3. It may
;; be related to the BarBazUtil upgrade. (xz 13-1-31)
(baz))
```
*
В случае, когда проблема настолько очевидна, что ее описание будет
лишним, аннотации могут быть оставлены в конце проблемной строки в виде
ключевого слова без пояснения. Такое использование должно быть исключением, но
не правилом.
[[link](#rare-eol-annotations)]
```Clojure
(defn bar
[]
(sleep 100)) ; OPTIMIZE
```
*
Используйте `TODO`, чтобы отметить недостающие фичи или функциональность,
которую нужно будет добавить позже.
[[link](#todo)]
*
Используйте `FIXME`, чтобы отметить сломанный код, который нужно исправить.
[[link](#fixme)]
*
Используйте `OPTIMIZE`, чтобы отметить медленный или неэффективный код,
который может вызвать проблемы с производительностью.
[[link](#optimize)]
*
Используйте `HACK`, чтобы отметить "пахнущий" код (костыли,
прим. переводчика), который использует сомнительные приемы и должен быть
отрефакторен.
[[link](#hack)]
*
Используйте `REVIEW`, чтобы отметить то, с чем нужно внимательнее
ознакомиться, чтобы убедиться, что оно работает так, как нужно. Например:
`REVIEW: Are we sure this is how the client does X currently?`
[[link](#review)]
*
Используйте другие ключевые слова для аннотаций, если они кажутся приемлемыми.
Но не забудьте добавить их в `README` вашего проекта.
[[link](#document-annotations)]
## Документирование
Док. строки - основной способ документирования Clojure кода. Многие
формы-определения (например, `def`, `defn`, `defmacro`, `ns`) поддерживают
док. строки и использовать их - хорошая идея вне зависимости от того, является
ли описываемое публичным или приватным.
Если форма-определение не поддерживает док. строки, то вы все равно можете
добавить документацию с помощью аттрибута метаданных `:doc`.
В этом разделе выделены некоторые общие практики по документированию Clojure кода.
*
Если возможно, используйте док. строки вместо `:doc`.
[[link](#prefer-docstrings)]
```clojure
;; хорошо
(defn foo
"This function doesn't do much."
[]
...)
(ns foo.bar.core
"That's an awesome library.")
;; плохо
(defn foo
^{:doc "This function doesn't do much."}
[]
...)
(ns ^{:doc "That's an awesome library.")
foo.bar.core)
```
*
Пишите док. строки законченными предложениями с большой буквы, разумно
описывающими сущность, к которой привязаны. Благодаря этому ваши инструменты
(текстовые редакторы и IDE) смогут отображать краткое описание сущности во
многих местах.
[[link](#docstring-summary)]
```clojure
;; хорошо
(defn frobnitz
"This function does a frobnitz.
It will do gnorwatz to achieve this, but only under certain
cricumstances."
[]
...)
;; плохо
(defn frobnitz
"This function does a frobnitz. It will do gnorwatz to
achieve this, but only under certain cricumstances."
[]
...)
```
*
Документируйте все позиционные параметры и оборачивайте их с помощью обратных
ковычек (\`). Таким образом, текстовые редакторы и IDE смогут определять их
(параметры) и предоставлять дополнительную функциональность для них.
[[link](#document-pos-arguments)]
```clojure
;; хорошо
(defn watsitz
"Watsitz takes a `frob` and converts it to a znoot.
When the `frob` is negative, the znoot becomes angry."
[frob]
...)
;; плохо
(defn watsitz
"Watsitz takes a frob and converts it to a znoot.
When the frob is negative, the znoot becomes angry."
[frob]
...)
```
*
Оборачивайте ссылки на другие сущности (функции, константы) в обратные
\``ковычки`\`, чтобы ваши инструменты могли определять их.
[[link](#document-references)]
```clojure
;; хорошо
(defn wombat
"Acts much like `clojure.core/identity` except when it doesn't.
Takes `x` as an argument and returns that. If it feels like it."
[x]
...)
;; плохо
(defn wombat
"Acts much like clojure.core/identity except when it doesn't.
Takes `x` as an argument and returns that. If it feels like it."
[x]
...)
```
*
Док.строки должны быть грамотно оформленными предложениями на английском
языке, т.е. каждое предложение должно начинаться с прописной буквы и
заканчиваться точкой (или другим подходящим знаком препинания). Предложения
должны отделяться пробелом.
[[link](#docstring-grammar)]
```clojure
;; хорошо
(def foo
"All sentences should end with a period (or maybe an exclamation mark).
And the period should be followed by a space, unless it's the last sentence.")
;; плохо
(def foo
"all sentences should end with a period (or maybe an exclamation mark).
And the period should be followed by a space, unless it's the last sentence")
```
*
Оставляйте отступ в два пробела в многострочных док. строках.
[[link](#docstring-indentation)]
```clojure
;; хорошо
(ns my.ns
"It is actually possible to document a ns.
It's a nice place to describe the purpose of the namespace and maybe even
the overall conventions used. Note how _not_ indenting the doc string makes
it easier for tooling to display it correctly.")
;; плохо
(ns my.ns
"It is actually possible to document a ns.
It's a nice place to describe the purpose of the namespace and maybe even
the overall conventions used. Note how _not_ indenting the doc string makes
it easier for tooling to display it correctly.")
```
*
Не начинайте и не заканчивайте док. строки пробелом.
[[link](#docstring-leading-trailing-whitespace)]
```clojure
;; хорошо
(def foo
"I'm so awesome."
42)
;; плохо
(def silly
" It's just silly to start a doc string with spaces.
Just as silly as it is to end it with a bunch of them. "
42)
```
*
Когда пишете док. строку, размещайте его *после* имени функции, а не
после списка параметров. Последнее не является нарушением синтаксиса и не
вызовет никаких ошибок, но включает строку как часть тела функции без привязки
к документации.
[[link](#docstring-after-fn-name)]
```Clojure
;; хорошо
(defn foo
"docstring"
[x]
(bar x))
;; плохо
(defn foo [x]
"docstring"
(bar x))
```
## Общие рекомендации
*
Пишите в функциональном стиле, используя изменяемое состояние тогда, когда это
необходимо.
[[link](#be-functional)]
*
Будьте последовательны. В идеале, будьте последовательны по этому руководству.
[[link](#be-consistent)]
*
Используйте здравый смысл.
[[link](#common-sense)]
## Инструменты
Есть несколько полезных штук, созданных Clojure сообществом, которые могут
помочь вам в вашем стремлении писать хороший Clojure код.
* [Slamhound](https://github.com/technomancy/slamhound) - инструмент, который
будет генерировать верные `ns` определения из существующего кода.
* [kibit](https://github.com/jonase/kibit) - статический анализатор кода для
Clojure, который использует [core.logic](https://github.com/clojure/core.logic)
для поиска кода, который можно переписать с помощью существующей функции или
макроса.
## Тестирование
*
Храните ваши тесты в отдельной директории, обычно `test/yourproject` (в
контрасте с `src/yourproject`). Ваш инструмент сборки (build tool) должен
обеспечить доступ к ним, когда это необходимо. Большинство шаблонов работают
с тестами автоматически.
[[link](#test-directory-structure)]
*
Именуйте пространство имен теста как `yourproject.something-test`, файл
обычно будет выглядеть как `test/yourproject/something_test.clj` (или
`.cljc`, `cljs`).
[[link](#test-ns-naming)]
*
При использовании `clojure.test`, определяйте ваши тесты с помощью `deftest`
и называйте их `something-test`.
[[link](#test-naming)]
```clojure
;; хорошо
(deftest something-test ...)
;; плохо
(deftest something-tests ...)
(deftest test-something ...)
(deftest something ...)
```
## Создание библиотек
*
Если вы публикуете библиотеки, используемые другими людьми, обязательно
следуйте
[руководству Central Repository](http://central.sonatype.org/pages/choosing-your-coordinates.html)
при выборе `groupId` и `artifactId`. Это позволит предотвратить конфликты
имен и облегчить широкое ее использование. Хорошим примером является
[Component](https://github.com/stuartsierra/component).
[[link](#lib-coordinates)]
*
Избегайте ненужных зависимостей. Например, трехстрочная вспомогательная
функция, скопированная в проект обычно лучше, чем зависисмость, тянущая за
собой сотни наименований, которые вы не собираетесь использовать.
[[link](#lib-min-dependencies)]
*
Предоставляйте основную функциональность и различные интеграции в отдельных
артефактах. В таком случае, пользователи смогут воспользоваться вашей
библиотекой, не ограничивая себя вашими инструментальными предпочтениями.
Например, [Component](https://github.com/stuartsierra/component)
предоставляет основной функционал, а
[reloaded](https://github.com/stuartsierra/reloaded) предоставляет интеграцию
с leiningen.
[[link](#lib-core-separate-from-tools)]
# Помощь проекту
Написанное в этом руководстве не является истинной последней инстанции. Я хочу
работать совместно с каждым, кто заинтересован в хорошем оформлении кода
Clojure, чтобы мы смогли создать ресурс, полезный для всего Clojure сообщества.
Не стесняйтесь открывать тикеты и создавать pull-реквесты с улучшениями. Заранее
спасибо за помощь!
Также вы можете помочь руководству финансово с помощью
[gittip](https://www.gittip.com/bbatsov).
[![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/bbatsov)
Примечание переводчика:
Данный репозиторий содержит лишь перевод
[оригинального руководства](https://github.com/bbatsov/clojure-style-guide).
Соответственно, все вопросы и предложения, не касающиеся конкретно перевода,
должны направляться в оригинальный репозиторий.
# License
![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png)
Данное руководство использует лицензию
[Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/deed.en_US)
# Расскажите всем
Руководство, написанное сообществом, не может оказывать много помощи сообществу,
которое не знает о его существовании. Расскажите о данном гайде друзьям и
коллегам. Каждый комментарий, каждое предложение и мнение, которые мы получаем,
делают это руководство немного лучше. А мы хотим иметь наилучшее возможное
руководство, не правда ли?
Удачи,
[Божидар](https://twitter.com/bbatsov)