Skip to content

A community coding style guide for the Clojure programming language

Notifications You must be signed in to change notification settings

rodriguescelio/clojure-style-guide

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 

Repository files navigation

Guia de estilo Clojure

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:

Índice

Disposição do código & Organização

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 like when, 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 defs 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 and cond. [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 of refers, requires, and imports, 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]

Sintaxe

  • Evite o uso de funções de manipulação de namespace como require e refer. 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 de loop/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 de def 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 de into 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 de let + 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 de let + 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? e odd?).

  • 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 em cond. [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 de cond 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 de cond ou condp 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 chamadas cons 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

Nomenclaturas

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 de to 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 como pred e coll. [link]

    • em funções:
      • f, g, h - input da função
      • n - input inteiro, normalmente tamanho
      • index, i - index inteiro
      • x, y - números
      • xs - sequência
      • m - map
      • s - input string
      • re - expressão regular
      • coll - uma coleção
      • pred - um fechamento de predicado
      • & more - input variante
      • xf - xform, um transducer
    • em macros:
      • expr - uma expressão
      • body - o corpo de uma macro
      • binding - um vetor binding de uma macro

Collections

É 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]

Mutações

Refs

  • 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]

Agentes

  • 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]

Atoms

  • Evite atualizações de atom dentro de transações STM. [link]

  • Tente usar swap! no lugar de reset! onde for possível. [link]

    (def a (atom 0))
    
    ;; bom
    (swap! a + 5)
    
    ;; not as good
    (reset! a 5)

Strings

  • 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")

Exceções

  • 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 a finally. [link]

Macros

  • 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]

Comentários

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 de Comentários

  • 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]

Documentação

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))

Existencial

  • 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]

Ferramentas

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.

Testando

  • Armazene seus testes em um diretório separado, normalmente test/seuprojeto/ (em oposição a src/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á em test/seuprojeto/algumacoisa_test.clj (ou .cljc, cljs). [link]

  • Ao usar clojure.test, defina seus testes com deftest and nomeie com algumacoisa-test. For example:

    ;; bom
    (deftest algumacoisa-test ...)
    
    ;; ruim
    (deftest algumacoisa-tests ...)
    (deftest test-algumacoisa ...)
    (deftest algumacoisa ...)

    [link]

Library Organization

  • If you are publishing libraries to be used by others, make sure to follow the Central Repository guidelines for choosing your groupId and artifactId. 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]

Contribuindo

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.

Apoie via Gittip

Licença

Creative Commons License Este trabalho é licenciado sob Creative Commons Attribution 3.0 Unported License

Espalhe a Palavra

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

About

A community coding style guide for the Clojure programming language

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published