Os modelos são importantes.
-- Oficial Alex J. Murphy / RoboCop
Este guia de estilo recomenda melhores práticas para que programadores Clojure possam escrever códigos e possam ser mantidos por outros programadores. A style guide that reflects real-world usage gets used, and a style guide that holds to an ideal that has been rejected by the people it is supposed to help risks not getting used at all — não importa quão bom ele seja.
O guia está dividido em várias seções de regras relacionadas. Eu tentei adicionar toda a lógica por trás das regras (Se for omitido, estou assumindo que é bastante óbvio).
Eu não apareci com todas as regras do nada; elas foram feitas principalmente com base na minha extensa carreira como engenheiro de software profissional, feedback e sugestões de membros da comunidade Clojure, and various highly regarded Clojure programming resources, como "Clojure Programming" e "The Joy of Clojure".
O guia ainda é um trabalho em andamento; está faltando algumas seções, outras estão incompletas, algumas regras faltam exemplos, outras não tem exemplos que as ilustre bem o suficiente. Estas questões seram abordadas em seu devido tempo — Apenas mantenha-os em mente por enquanto.
Por favor, notem que a comunidade de desenvolvedores do Clojure também mantém uma lista de padrões de codificação para as bibliotecas.
Você pode gerar uma cópia em PDF ou em HTML deste guia usando o Pandoc.
Traduções deste guia estão disponíveis também nas seguintes linguagens:
- Disposição do código & Organização
- Sintaxe
- Nomenclaturas
- Collections
- Mutações
- Strings
- Exceções
- Macros
- Comentários
- Existencial
- Ferramentas
- Testando
- Documentação
- Organização das bibliotecas
Nearly everybody is convinced that every style but their own is ugly and unreadable. Leave out the "but their own" and they're probably right...
-- Jerry Coffin (on indentation)
-
Use espaços na identação. Não use tab. [link]
-
Use 2 spaces to indent the bodies of forms that have body parameters. This covers all
def
forms, special forms and macros that introduce local bindings (e.g.loop
,let
,when-let
) and many macros likewhen
,cond
,as->
,cond->
,case
,with-*
, etc. [link];; bom (when alguma-coisa (outra-coisa)) (with-out-str (println "Oĺa, ") (println "mundo!")) ;; ruim - quatro espaços (when alguma-coisa (outra-coisa)) ;; ruim - um espaço (with-out-str (println "Oĺá, ") (println "mundo!"))
-
Alinhar verticalmente argumentos da função (macro) distribuída em várias linhas. [link]
;; bom (filter even? (range 1 10)) ;; ruim (filter even? (range 1 10))
-
Use um único espaço para os argumentos da função quando não há argumentos na mesma linha que o nome da função. [link]
;; bom (filter even? (range 1 10)) (or ala bala portokala) ;; ruim - dois espaços (filter even? (range 1 10)) (or ala bala portokala)
-
Alinhar verticalmente ligações
let
e keyword maps. [link];; bom (let [coisa1 "alguma coisa" coisa2 "outra coisa"] {:coisa1 coisa1 :coisa2 coisa2}) ;; ruim (let [coisa1 "alguma coisa" coisa2 "outra coisa"] {:coisa1 coisa1 :coisa2 coisa2})
-
Opcionalmente, omita a nova linha entre o nome da função e o vetor de argumento para
defn
quando não há docstring. [link];; bom (defn foo [x] (bar x)) ;; bom (defn foo [x] (bar x)) ;; ruim (defn foo [x] (bar x))
-
Coloque a
dispatch-val
de um método múltiplo na mesma linha que o nome da função. [link];; bom (defmethod foo :bar [x] (baz x)) (defmethod foo :bar [x] (baz x)) ;; ruim (defmethod foo :bar [x] (baz x)) (defmethod foo :bar [x] (baz x))
-
Opcionalmente, omita a nova linha entre o vetor de argumento e um corpo de função curto. [link]
;; bom (defn foo [x] (bar x)) ;; bom for a small function body (defn foo [x] (bar x)) ;; bom for multi-arity functions (defn foo ([x] (bar x)) ([x y] (if (predicate? x) (bar x) (baz x)))) ;; ruim (defn foo [x] (if (predicate? x) (bar x) (baz x)))
-
Indente cada forma de aridade de uma definição de função alinhada verticalmente com seus parâmetros.[link]
;; bom (defn foo "I have two arities." ([x] (foo x 1)) ([x y] (+ x y))) ;; ruim - extra indentation (defn foo "I have two arities." ([x] (foo x 1)) ([x y] (+ x y)))
-
Sort the arities of a function from fewest to most arguments. The common case of multi-arity functions is that some K arguments fully specifies the function's behavior, and that arities N < K partially apply the K arity, and arities N > K provide a fold of the K arity over varargs. [link]
;; bom - it's easy to scan for the nth arity (defn foo "I have two arities." ([x] (foo x 1)) ([x y] (+ x y))) ;; okay - the other arities are applications of the two-arity (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))) ;; ruim - unordered for no apparent reason (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)))
-
Use Unix-style line endings. (*BSD/Solaris/Linux/OSX users are covered by default, Windows users have to be extra careful.) [link]
- If you're using Git you might want to add the following configuration setting to protect your project from Windows line endings creeping in:
bash$ git config --global core.autocrlf true
-
If any text precedes an opening bracket(
(
,{
and[
) or follows a closing bracket()
,}
and]
), separate that text from that bracket with a space. Conversely, leave no space after an opening bracket and before following text, or after preceding text and before a closing bracket. [link];; bom (foo (bar baz) quux) ;; ruim (foo(bar baz)quux) (foo ( bar baz ) quux)
Syntactic sugar causes semicolon cancer.
-- Alan Perlis
-
Don't use commas between the elements of sequential collection literals. [link]
;; bom [1 2 3] (1 2 3) ;; ruim [1, 2, 3] (1, 2, 3)
-
Consider enhancing the readability of map literals via judicious use of commas and line breaks. [link]
;; bom {:name "Bruce Wayne" :alter-ego "Batman"} ;; bom and arguably a bit more readable {:name "Bruce Wayne" :alter-ego "Batman"} ;; bom and arguably more compact {:name "Bruce Wayne", :alter-ego "Batman"}
-
Place all trailing parentheses on a single line instead of distinct lines. [link]
;; bom; single line (when something (something-else)) ;; ruim; distinct lines (when something (something-else) )
-
Use empty lines between top-level forms. [link]
;; bom (def x ...) (defn foo ...) ;; ruim (def x ...) (defn foo ...)
An exception to the rule is the grouping of related
def
s together.;; bom (def min-rows 10) (def max-rows 20) (def min-cols 15) (def max-cols 30)
-
Do not place blank lines in the middle of a function or macro definition. An exception can be made to indicate grouping of pairwise constructs as found in e.g.
let
andcond
. [link] -
Where feasible, avoid making lines longer than 80 characters. [link]
-
Avoid trailing whitespace. [link]
-
Use one file per namespace. [link]
-
Start every namespace with a comprehensive
ns
form, comprised ofrefer
s,require
s, andimport
s, conventionally in that order. [link](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]))
-
In the
ns
form prefer:require :as
over:require :refer
over:require :refer :all
. Prefer:require
over:use
; the latter form should be considered deprecated for new code. [link];; bom (ns examples.ns (:require [clojure.zip :as zip])) ;; bom (ns examples.ns (:require [clojure.zip :refer [lefts rights]])) ;; acceptable as warranted (ns examples.ns (:require [clojure.zip :refer :all])) ;; ruim (ns examples.ns (:use clojure.zip))
-
Avoid single-segment namespaces. [link]
;; bom (ns example.ns) ;; ruim (ns example)
-
Avoid the use of overly long namespaces (i.e., more than 5 segments). [link]
-
Avoid functions longer than 10 LOC (lines of code). Ideally, most functions will be shorter than 5 LOC. [link]
-
Avoid parameter lists with more than three or four positional parameters. [link]
-
Avoid forward references. They are occasionally necessary, but such occasions are rare in practice. [link]
-
Evite o uso de funções de manipulação de namespace como
require
erefer
. Elas são desnecessárias fora do ambiente REPL. [link] -
Use
declare
para habilitar referências futuras quando elas forem necessárias. [link] -
Prefira funções de alta ordem como
map
no lugar deloop/recur
. [link] -
Prefira funções de pre e pos condições para fazer checagens dentro do corpo de uma função. [link]
;; bom (defn foo [x] {:pre [(pos? x)]} (bar x)) ;; ruim (defn foo [x] (if (pos? x) (bar x) (throw (IllegalArgumentException. "x deve ser um número positivo!")))
-
Não defina variáveis dentro de funções. [link]
;; muito ruim (defn foo [] (def x 5) ...)
-
Não sobrescreva nomes
clojure.core
com atribuições locais. [link];; ruim - você é forçado a usar clojure.core/map com todo namespace (defn foo [map] ...)
-
Use
alter-var-root
no lugar dedef
para alterar o valor de variáveis. [link];; bom (def variável 1) ; o valor de coisa agora é 1 ; faz algo com variável (alter-var-root #'variável (constantly nil)) ; valor de variável agora é nil ;; ruim (def variável 1) ; faz algo com variável (def variável nil) ; valor de variável é nil agora
-
Use
seq
como uma condição terminal para verificar quando uma sequência é vazia (essa técnica é as vezes chamada de nil punning). [link];; bom (defn print-seq [s] (when (seq s) (prn (first s)) (recur (rest s)))) ;; ruim (defn print-seq [s] (when-not (empty? s) (prn (first s)) (recur (rest s))))
-
Prefira
vec
no lugar deinto
que precisar converter uma sequância em um vetor. [link];; bom (vec uma-seq) ;; ruim (into [] uma-seq)
-
Use
when
instead of(if ... (do ...))
. [link];; bom (when pred (foo) (bar)) ;; ruim (if pred (do (foo) (bar)))
-
Use
if-let
no lugar delet
+if
. [link];; bom (if-let [resultado (foo x)] (algo-com resultado) (oura-coisa)) ;; ruim (let [resultado (foo x)] (if resultado (algo-com resultado) (outra-coisa)))
-
Use
when-let
no lugar delet
+when
. [link];; bom (when-let [resultado (foo x)] (faz-algo-com resultado) (faz-algo-mais-com resultado)) ;; ruim (let [resultado (foo x)] (when resultado (faz-algo-com resultado) (faz-algo-mais-com resultado)))
-
Use
if-not
no lugar de(if (not ...) ...)
. [link];; bom (if-not pred (foo)) ;; ruim (if (not pred) (foo))
-
Use
when-not
no lugar de(when (not ...) ...)
. [link];; bom (when-not pred (foo) (bar)) ;; ruim (when (not pred) (foo) (bar))
-
Use
when-not
instead of(if-not ... (do ...))
. [link];; bom (when-not pred (foo) (bar)) ;; ruim (if-not pred (do (foo) (bar)))
-
Use
not=
no lugar de(not (= ...))
. [link];; bom (not= foo bar) ;; ruim (not (= foo bar))
-
Use
printf
no lugar de(print (format ...))
. [link];; bom (printf "Olá, %s!\n" nome) ;; ok (println (format "Olá, %s!" nome))
-
Quando fizer comparações, tenha em mente que as funções Clojure
<
,>
, etc. aceitam um número variável de argumentos. [link];; bom (< 5 x 10) ;; ruim (and (> x 5) (< x 10))
-
Prefira
%
no lugar de%1
nos argumentos de funções com apenas um parâmetro. [link];; bom #(Math/round %) ;; ruim #(Math/round %1)
-
Prefira
%1
no lugar de%
nos argumentos de funções com mais de um parâmetro. [link];; bom #(Math/pow %1 %2) ;; ruim #(Math/pow % %2)
-
Não envolva funções com funções anônimas sem necessidade. [link]
;; bom (filter even? (range 1 10)) ;; ruim (filter #(even? %) (range 1 10))
-
Não use literais de uma função se o corpo da função vai consistir em mais de uma chamada. [link]
;; bom (fn [x] (println x) (* x 2)) ;; ruim (você precisa explicitamente de uma chamada do) #(do (println %) (* % 2))
-
Prefira o uso de
complement
no lugar de uma função anônima. [link];; bom (filter (complement some-pred?) coll) ;; ruim (filter #(not (some-pred? %)) coll)
Essa regra deve obviamente ser ignorada se já existir uma função que faz o mesmo que usar o complemento (e.g.
even?
eodd?
). -
Usar
comp
pode deixar o código mais simples. [link];; Assumindo `(:require [clojure.string :as str])`... ;; bom (map #(str/capitalize (str/trim %)) ["top " " test "]) ;; melhor (map (comp str/capitalize str/trim) ["top " " test "])
-
Usar
partial
pode deixar o código mais simples. [link];; bom (map #(+ 5 %) (range 1 10)) ;; (dicutivelmente) melhor (map (partial + 5) (range 1 10))
-
Prefira o uso de threading macros
->
(thread-first) e->>
(thread-last) no lugar de aninhamentos exaustivos. [link];; bom (-> [1 2 3] reverse (conj 4) prn) ;; não muito bom (prn (conj (reverse [1 2 3]) 4)) ;; bom (->> (range 1 10) (filter even?) (map (partial * 2))) ;; não muito bom (map (partial * 2) (filter even? (range 1 10)))
-
Use
:else
como um teste de experessão catch-all emcond
. [link];; bom (cond (neg? n) "negativo" (pos? n) "positivo" :else "zero") ;; ruim (cond (neg? n) "negativo" (pos? n) "positivo" true "zero")
-
Prefira
condp
no lugar decond
quando o predicado e expressão não mudarem. [link];; bom (cond (= x 10) :dez (= x 20) :vinte (= x 30) :trinta :else :nao-sei) ;; muito melhor (condp = x 10 :dez 20 :vinte 30 :trinta :nao-sei)
-
Prefira
case
no lugar decond
oucondp
quando expressões de checagens são constantes de tempo de compilação. [link];; bom (cond (= x 10) :dez (= x 20) :vinte (= x 30) :trinta :else :nao-sei) ;; melhor que anterior (condp = x 10 :dez 20 :vinte 30 :trinta :nao-sei) ;; melhor caso (case x 10 :dez 20 :vinte 30 :trinta :nao-sei)
-
Use formas curtas no
cond
e similares. Se não for possível dê dicar visuais para as assosiações com comentário ou linhas em branco. [link];; bom (cond (test1) (acao-1) (test2) (acao-2) :else (action-padrao)) ;; ok (cond ;; test caso 1 (test1) (funcao-com-nome-longo-que-requer-uma-nova-linha (sub-forma-complicada (-> 'que-abrange multiplas-linhas))) ;; test caso 2 (test2) (outra-funcao-de-nome-grande (outra-sub-forma (-> 'que-abrange multiplas-linhas))) :else (o-caso-padrao (que-tambem-abrange 'multiplas 'linhas)))
-
Use um
set
como predicado quando apropriado. [link];; bom (remove #{1} [0 1 2 3 4 5]) ;; ruim (remove #(= % 1) [0 1 2 3 4 5]) ;; bom (count (filter #{\a \e \i \o \u} "mary had a little lamb")) ;; ruim (count (filter #(or (= % \a) (= % \e) (= % \i) (= % \o) (= % \u)) "mary had a little lamb"))
-
Use
(inc x)
&(dec x)
no lugar de(+ x 1)
e(- x 1)
. [link] -
Use
(pos? x)
,(neg? x)
&(zero? x)
no lugar de(> x 0)
,(< x 0)
&(= x 0)
. [link] -
Use
list*
no lugar de uma serie de chamadascons
aninhadas. [link];; bom (list* 1 2 3 [4 5]) ;; ruim (cons 1 (cons 2 (cons 3 [4 5])))
-
Use os formulários de interoperabilidade Java. [link]
;;; criação do objeto ;; bom (java.util.ArrayList. 100) ;; ruim (new java.util.ArrayList 100) ;;; chamada de método estático ;; bom (Math/pow 2 10) ;; ruim (. Math pow 2 10) ;;; chamada de instância de método ;; bom (.substring "ola" 1 3) ;; ruim (. "ola" substring 1 3) ;;; acesso a campo estático ;; bom Integer/MAX_VALUE ;; ruim (. Integer MAX_VALUE) ;;; acesso a instância de campo ;; bom (.umCampo um-objeto) ;; ruim (. um-objeto umObjeto)
-
Use a notação compacta de metadado para metadados de contém apenas slots em que as keys são keywords e o valor é booleano
true
. [link];; bom (def ^:private a 5) ;; ruim (def ^{:private true} a 5)
-
Denote partes privadas do seu código. [link]
;; bom (defn- funcao-privada [] ...) (def ^:private variavel-privada ...) ;; ruim (defn funcao-privda [] ...) ; Não é uma função privada (defn ^:private funcao-privada [] ...) ; muito verbosa (def variavel-privada ...) ; Não é privada
-
Para acessar uma variável privada (e.g. para testes), use a notação
@#'some.ns/var
. [link] -
Tenha cuidado ao que você associa seu metadado exatamente. [link]
;; nós associamos o metadado a variável referenciada por `a` (def ^:private a {}) (meta a) ;=> nil (meta #'a) ;=> {:private true} ;; nós associamos o metadado ao valor vazio do hash-map (def a ^:private {}) (meta a) ;=> {:private true} (meta #'a) ;=> nil
As únicas reais dificuldades em programação são invalidação de cache e nomear coisas.
-- Phil Karlton
-
Ao nomear namespaces escolha entre os dois seguintes schemas: [link]
project.module
organization.project.module
-
Use
lisp-case
em segmentos de namespace compostos(e.g.bruce.project-euler
) [link] -
Use
lisp-case
em funções e nomes de variáveis. [link];; bom (def uma-var ...) (defn uma-fun ...) ;; ruim (def umaVar ...) (defn umafun ...) (def uma_fun ...)
-
Use
CamelCase
para protocolos, registros, estruturas e tipos. (Mantenha acrônimos como HTTP, RFC, XML em maiúsculo.) [link] -
Os nomes dos métodos predicados (métodos que retornam um valor booleano) devem terminar em um ponto de interrogação (e.g.,
impar?
). [link];; bom (defn palindrome? ...) ;; ruim (defn palindrome-p ...) ; estilo Common Lisp (defn is-palindrome ...) ; estilo Java
-
Os nomes de funções/macros que não são "safe" em transações STM devem terminar com uma ponto de exclamação (e.g.
reset!
). [link] -
Use
->
no lugar deto
ao nomear funções de conversão. [link];; bom (defn f->c ...) ;; não muito bom (defn f-to-c ...)
-
Use
*earmuffs*
para coisas destinatas a rebinding (ou seja, são dinâmicas). [link];; bom (def ^:dynamic *a* 10) ;; ruim (def ^:dynamic a 10)
-
Não use notações especiais para constantes; tudo é constante a não ser que seja especificado do contrário. [link]
-
Use
_
para destructuring e nomes formais para argumentos que terão seus valores ignorado pelo código em mãos. [link];; bom (let [[a b _ c] [1 2 3 4]] (println a b c)) (dotimes [_ 3] (println "Hello!")) ;; ruim (let [[a b c d] [1 2 3 4]] (println a b d)) (dotimes [i 3] (println "Hello!"))
-
Siga o exemplo
clojure.core
's para nomes idiomáticos comopred
ecoll
. [link]- em funções:
f
,g
,h
- input da funçãon
- input inteiro, normalmente tamanhoindex
,i
- index inteirox
,y
- númerosxs
- sequênciam
- maps
- input stringre
- expressão regularcoll
- uma coleçãopred
- um fechamento de predicado& more
- input variantexf
- xform, um transducer
- em macros:
expr
- uma expressãobody
- o corpo de uma macrobinding
- um vetor binding de uma macro
- em funções:
É melhor ter 100 funções operando em uma estrutura de dados do que ter 10 funções operando em 10 estrutura de dados.
-- Alan J. Perlis
-
Evite o uso de listas para armazenamento genérico de dados (a menos que uma seja exatamente o que você precisa). [link]
-
Prefira o uso de keywords para hash keys. [link]
;; bom {:nome "Bruce" :idade 30} ;; ruim {"nome" "Bruce" "idade" 30}
-
Prefira o uso da sintaxe da coleção literal quando aplicável. No entanto, ao definir conjuntos, apenas use a sintaxe literal quando os valores forem constantes de tempo de compilação. [link]
;; bom [1 2 3] #{1 2 3} (hash-set (func1) (func2)) ; valores determinados em tempo de execução ;; ruim (vector 1 2 3) (hash-set 1 2 3) #{(func1) (func2)} ; irá lançar exceção de tempo de execução se (func1) = (func2)
-
Evite acessar os membros da coleção por índice sempre que possível. [link]
-
Prefira o uso de keywords como funções para recuperar valores de mapas, quando aplicável. [link]
(def m {:nome "Bruce" :idade 30}) ;; bom (:nome m) ;; mais detalhado que o necessário (get m :nome) ;; ruim - suscetível a NullPointerException (m :nome)
-
Aproveite o fato de que a maioria das coleções são funções de seus elementos. [link]
;; bom (filter #{\a \e \o \i \u} "isso é um teste") ;; ruim - muito feio para compartilhar
-
Aproveite o fato de que keywords podem ser usadas como funções de uma coleção. [link]
((juxt :a :b) {:a "ala" :b "bala"})
-
Evite o uso de coleções transitórias, exceto em partes críticas de desempenho do código. [link]
-
Evite o uso de Java collections. [link]
-
Evite o uso de arrays de of Java arrays, exceto para cenários de interoperabilidade e código de desempenho crítico lidando fortemente com tipos primitivos. [link]
-
Considere agrupar todas as chamadas I/O com a macro
io!
para evitar surpresas desagradáveis se acidentalmente acabar chamando esse código em uma transação. [link] -
Evite o uso de
ref-set
sempre que possível. [link](def r (ref 0)) ;; bom (dosync (alter r + 5)) ;; ruim (dosync (ref-set r 5))
-
Tente manter o tamanho das transações (a quantidade de trabalho encapsulado nelas) o menor possível. [link]
-
Evite que transações de curta e longa duração interajam com a mesma Ref. [link]
-
Use
send
somente para ações que são ligadas à CPU e não bloqueiam em I/O ou outras threads. [link] -
Use
send-off
para ações que possam bloquear, suspender ou amarrar a thread. [link]
-
Evite atualizações de atom dentro de transações STM. [link]
-
Tente usar
swap!
no lugar dereset!
onde for possível. [link](def a (atom 0)) ;; bom (swap! a + 5) ;; not as good (reset! a 5)
-
Prefira as funções de manipulação de strings do
clojure.string
do que a interoperabilidade Java ou escrever sua própria. [link];; bom (clojure.string/upper-case "bruce") ;; ruim (.toUpperCase "bruce")
-
Reutilize os tipos de exceção existentes. O código idiomático Clojure — quando lança uma exceção — lança uma exceção de um tipo padrão (por exemplo,
java.lang.IllegalArgumentException
,java.lang.UnsupportedOperationException
,java.lang.IllegalStateException
,java.io.IOException
). [link] -
Prefira
with-open
afinally
. [link]
-
Não escreva uma macro se uma função faz o mesmo. [link]
-
Crie um exemplo de uso de macro primeiro e depois a macro. [link]
-
Quebre as macros complicadas em funções menores sempre que possível. [link]
-
Uma macro geralmente deve fornecer apenas açúcar sintático e o núcleo da macro deve ser uma função simples. Isso aumentará a composibilidade. [link]
-
Prefira formulários entre aspas de sintaxe a construir listas manualmente. [link]
Um bom código é sua melhor documentação. Quando estiver prestes a adicionar um comentário, pergunte a si mesmo, "Como eu posso melhorar o código de maneira que esse comentário não seja necessário?" Melhore o código e então o documente para deixá-lo ainda mais claro.
-- Steve McConnell
-
Se esforce para tornar seu código o mais auto-documentável possível. [link]
-
Escreva comentários de cabeçalho com pelo menos quatro ponto e vírgulas. [link]
-
Escreva comentários top-level com três ponto e vírgulas. [link]
-
Escreva comentários em um fragmento particular de código antes do fragmento e alinhado com o mesmo, utilizando dois ponto e vírgulas. [link]
-
Escreva comentários de margem com um ponto e vírgula. [link]
-
Sempre tenha pelo menos um espaço entre o ponto e vírgula e o texto que o segue. [link]
;;;; Frob Grovel ;;; Esse código tem algumas implicações importantes: ;;; 1. Foo. ;;; 2. Bar. ;;; 3. Baz. (defn fnord [zarquon] ;; Se zob, então veeblefitz. (quux zot mumble ; Zibblefrotz. frotz))
-
Comentários maiores que uma palavra começam com letra maiúscula e usam pontuação. Sentenças separadas com um espaço. [link]
-
Evite comentários supérfulos. [link]
;; ruim (inc counter) ; incrementa contador em um
-
Mantenha os comentários existentes atualizados. Um comentário desatualizado é pior que nenhum comentário. [link]
-
Prefira o uso do macro leitor
#_
no lugar de um comentário normal quando precisar comentar um formulário específico. [link];; bom (+ foo #_(bar x) delta) ;; ruim (+ foo ;; (bar x) delta)
Um bom código é como uma boa piada - não precisa de explicação.
-- Russ Olsen
- Evite escrever comentários para explicar um código ruim. Refatore o código para torná-lo auto-explicativo. ("Faça ou não faça. Não há tentativa." --Yoda) [link]
-
Anotações devem geralmente ser escritas na linha imediatamente acima da linha do código de relevância. [link]
;; good (defn some-fun [] ;; FIXME: Replace baz with the newer bar. (baz)) ;; bad ;; FIXME: Replace baz with the newer bar. (defn some-fun [] (baz))
-
A palavra-chave de anotação é seguida por dois-pontos e um espaço, depois por uma nota descrevendo o problema. [link]
;; good (defn some-fun [] ;; FIXME: Replace baz with the newer bar. (baz)) ;; bad - no colon after annotation (defn some-fun [] ;; FIXME Replace baz with the newer bar. (baz)) ;; bad - no space after colon (defn some-fun [] ;; FIXME:Replace baz with the newer bar. (baz))
-
Se várias linhas forem necessárias para descrever o problema, as linhas subsequentes deverão ser indentadas tanto quanto a primeira. [link]
;; good (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)) ;; bad (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))
-
Marque a anotação com suas iniciais e uma data para que sua relevância possa ser facilmente verificada. [link]
(defn some-fun [] ;; FIXME: Isso quebra ocasionalmente desde v1.2.3. Isso pode ;; estar relacionado a atualização do BarBazUtil. (xz 13-1-31) (baz))
-
Em casos que o problema é tão óbvio que qualquer documentação se tornaria redundante, anotações podem ser deixadas no final da linha ofensiva sem anotações. Esse uso deve ser a exceção e não a regra. [link]
(defn bar [] (sleep 100)) ; OPTIMIZE
-
Use
TODO
para deixar uma observação de recursos ou funcionalidades ausentes que devem ser adicionados posteriormente. [link] -
Use
FIXME
para deixar uma observação de código quebrado que precisa ser corrigido. [link] -
Use
OPTIMIZE
para deixar uma observação de código lento ou ineficiente que pode causar problemas de desempenho. [link] -
Use
HACK
para deixar uma observação de to note "code smells" onde práticas de codificação questionáveis foram utilizadas e devem ser refatoradas. [link] -
Use
REVIEW
para deixar uma observação de qualquer coisa que deva ser analisada para confirmar se está funcionando conforme o esperado. Por exemplo:REVIEW: Temos certeza de que é assim que o cliente faz X atualmente?
[link] -
Use outras palavras-chave de anotação personalizadas se parecer apropriado, mas certifique-se de documentá-las no
README
do seu projeto ou similar. [link]
Docstrings are the primary way to document Clojure code. Many definition forms
(e.g. def
, defn
, defmacro
, ns
)
support docstrings and usually it's a good idea to make good use of them, regardless
of whether the var in question is something public or private.
If a definition form doesn't support docstrings directly you can still supply them via
the :doc
metadata attribute.
This section outlines some of the common conventions and best practices for documenting Clojure code.
-
If a form supports docstrings directly prefer them over using
:doc
metadata: [link]
;; good
(defn foo
"This function doesn't do much."
[]
...)
(ns foo.bar.core
"That's an awesome library.")
;; bad
(defn foo
^{:doc "This function doesn't do much."}
[]
...)
(ns ^{:doc "That's an awesome library.")
foo.bar.core)
- Let the first line in the doc string be a complete, capitalized sentence which concisely describes the var in question. This makes it easy for tooling (Clojure editors and IDEs) to display a short a summary of the docstring at various places. [link]
;; good
(defn frobnitz
"This function does a frobnitz.
It will do gnorwatz to achieve this, but only under certain
circumstances."
[]
...)
;; bad
(defn frobnitz
"This function does a frobnitz. It will do gnorwatz to
achieve this, but only under certain circumstances."
[]
...)
- Document all positional arguments, and wrap them them with backticks (`) so that editors and IDEs can identify them and potentially provide extra functionality for them. [link]
;; good
(defn watsitz
"Watsitz takes a `frob` and converts it to a znoot.
When the `frob` is negative, the znoot becomes angry."
[frob]
...)
;; bad
(defn watsitz
"Watsitz takes a frob and converts it to a znoot.
When the frob is negative, the znoot becomes angry."
[frob]
...)
- Wrap any var references in the docstring with ` so that tooling can identify them. [link]
;; good
(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]
...)
;; bad
(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]
...)
- Docstrings should be comprised from proper English sentences - this means every sentences should start with an capitalized word and should end with the proper punctuation. Sentences should be separated with a single space. [link]
;; good
(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.")
;; bad
(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")
- Indent multi-line docstrings by two spaces. [link]
;; good
(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.")
;; bad
(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.")
- Neither start nor end your doc strings with any whitespace. [link]
;; good
(def foo
"I'm so awesome."
42)
;; bad
(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)
- When adding a docstring – especially to a function using the above form – take care to correctly place the docstring after the function name, not after the argument vector. The latter is not invalid syntax and won’t cause an error, but includes the string as a form in the function body without attaching it to the var as documentation. [link]
;; good
(defn foo
"docstring"
[x]
(bar x))
;; bad
(defn foo [x]
"docstring"
(bar x))
-
Codifique de maneira funcional, usando mutação somente quando faz sentido. [link]
-
Seja consistente. Em um mundo ideal, seja consistente com essas diretrizes. [link]
-
Use o senso comum. [link]
Existem algumas ferramentas criadas pela comunidade Clojure que podem ajudá-lo em seu esforço em escrever o código de Clojure idiomático.
-
Slamhound é uma ferramenta que gerará automaticamente declarações
ns
adequadas a partir do seu código existente. -
kibit é um analisador de código estático para Clojure que usa core.logic para procurar padrões de código para os quais pode haver uma função ou macro mais idiomático.
-
Armazene seus testes em um diretório separado, normalmente
test/seuprojeto/
(em oposição asrc/seuprojeto/
). Sua ferramenta de compilação é responsável por disponibilizá-los nos contextos onde são necessários; a maioria dos modelos vai fazer isso automaticamente para você. [link] -
Nomeie seu ns
seuprojeto.algumacoisa-test
, um arquivo que geralmente está emtest/seuprojeto/algumacoisa_test.clj
(ou.cljc
,cljs
). [link] -
Ao usar
clojure.test
, defina seus testes comdeftest
and nomeie comalgumacoisa-test
. For example:;; bom (deftest algumacoisa-test ...) ;; ruim (deftest algumacoisa-tests ...) (deftest test-algumacoisa ...) (deftest algumacoisa ...)
[link]
-
If you are publishing libraries to be used by others, make sure to follow the Central Repository guidelines for choosing your
groupId
andartifactId
. This helps to prevent name conflicts and facilitates the widest possible use. A good example is Component. [link] -
Avoid unnecessary dependencies. For example, a three-line utility function copied into a project is usually better than a dependency that drags in hundreds of vars you do not plan to use. [link]
-
Deliver core functionality and integration points in separate artifacts. That way, consumers can consume your library without being constrained by your unrelated tooling prefences. For example, Component provides core functionality, and reloaded provides leiningen integration. [link]
Nada de escrito neste guia é definitivo. É meu desejo trabalhar em conjunto com todos os interessados no estilo de codificação em Clojure, para que possamos criar um recurso que seja benéfico para toda a comunidade Clojure.
Sinta-se à vontade para abrir tickets ou enviar pull requests com melhorias. Agradeço antecipadamente por sua ajuda!
Você também pode apoiar o guia de estilo com contribuições financeiras via gittip.
Este trabalho é licenciado sob Creative Commons Attribution 3.0 Unported License
Um guia de estilo dirigido pela comunidade é de pouca utilidade para uma comunidade que não sabe sobre sua existência. Tweet sobre o guia, compartilhe com seus amigos e colegas. Todos os comentários, sugestões ou opiniões que nós recebemos faz o guia um pouco melhor. E queremos ter o melhor guia possível, não é?
Felicidades,
Bozhidar