Skip to content

Latest commit

 

History

History
3137 lines (2348 loc) · 80.9 KB

README.adoc

File metadata and controls

3137 lines (2348 loc) · 80.9 KB

La guida allo stile per Clojure

I modelli da prendere in esempio sono importanti.

— Ufficiale Alex J. Murphy / RoboCop
Tip
Puoi trovare una bellissima versione di questa guida con una navigazione decisamente migliorata (in inglese) a https://guide.clojure.style.

Questa guida allo stile fornisce le best practice ai programmatori Clojure in modo che possano scrivere codice facilmente mantenibile da altri programmatori Clojure. Una guida allo stile che riflette l’utilizzo nel mondo reale viene utilizzata, e una guida allo stile che viene rifiutata dalle persone che dovrebbe aiutare rischia di non essere affatto utilizzata — non importa quanto sia fatta bene.

Questa guida è separata in diverse sezioni di regole correlate. Abbiamo provato ad includere il ragionamento dietro alle regole (se è stato omesso, abbiamo presento che fosse piuttosto ovvio).

Non abbiamo scovato tutte le regole dal nulla; sono basate soprattutto sull’esperienza degli editori della guida, sui feedback e sui consigli dei numerosi membri della community di Clojure, e altre fonti molto apprezzate per la programmazione Clojure come "Clojure Programming" e "The Joy of Clojure".

Nulla di ciò che viene scritto qui è inciso sulla pietra. Questa guida di stile si evolve con il passare del tempo, man mano che vengono identificate nuove convenzioni e quelle vecchie vengono rese obsolete dai cambiamenti nel liguaggio Clojure stesso.

Note
Gli sviluppatori Clojure mentengono anche una lista di linee guida per la programmazione con librerie.[1] Esse sono una delle fonti di ispirazione per il documento che stai leggendo.

Puoi generare una copia PDF di questa guida usando AsciiDoctor PDF, e una copia HTML con AsciiDoctor utilizzando i seguenti comandi:

# Genera README.pdf
asciidoctor-pdf -a allow-uri-read README.adoc

# Genera README.html
asciidoctor
Tip

Installa 'rouge' con gem per ottenere la colorazione del codice nel documento generato.

gem install rouge

I programmi devono essere scritti per le persone da leggere, e solo incidentalmente per le macchine da eseguire.

— Harold Abelson
Structure and Interpretation of Computer Programs

È risaputo che il codice viene letto molto più spesso di quanto viene scritto. Le linee guida fornite sono pensate per migliorare la leggibilità del codice e renderlo consistente (coerente) in tutto il largo spettro del codice Clojure. Sono anche pensate per riflettere l’utilizzo di Clojure nel mondo reale invece di un suo ideale. Quando abbiamo dovuto scegliere tra una pratica ormai stabilita e un alternativa soggetivamente migliore abbiamo optato per raccomandare la pratica stabilita.[2]

Ci sono alcune aree in cui non esiste un consenso generale chiaro nella community di Clojure riguardante un particolare stile (come intentazione fissa vs intentazione semantica, commenti uniformi, commenti semantici, etc). In questi scenari tutti gli stili popolari sono riconosciuti e viene lasciato a te sceglierne uno e applicarlo coerentemente.

Fortunatamente Clojure è un Lisp, e i Lisp sono fondamentalmente semplici. Anche se questa guida è stata creata qualche anno dopo Clojure (la prima versione è stata pubblicata all’inizio del 2013), si vedeva che il codice Clojure in natura era abbastanza uniforme. Attribuiamo questo sia alla semplicità di cui abbiamo già parlato sia al fatto che dal primo giorno i Clojuristi adottarono molte delle convenzioni stilistiche di altri dialetti Lisp stabiliti (ad esempio Common Lisp e Scheme). Questo rese il lavoro sulla guida è abbastanza semplice e diretto, soprattutto rispetto al massiccio sforzo e frustrazione che era la Community Ruby Style Guide.[3]

Clojure è notoriamente ottimizzato per semplicità e chiarezza. Ci piace credere che questa guida ti aiuterà a ottimizzare al massimo semplicità e chiarezza.

Una sciocca coerenza è l’hobgoblin delle piccole menti, adorato da piccoli politici, filosofi e teologi.

— Ralph Waldo Emerson

Una guida di stile riguarda la coerenza.[4] La coerenza con questa guida di stile è importante. La coerenza all’interno di un progetto è più importante. La coerenza all’interno di una classe o di un metodo è la cosa più importante.

Tuttavia, sappi quando essere incoerente: a volte i consigli della guida di stile non sono applicabili. In caso di dubbio, usa il tuo miglior giudizio. Guarda altri esempi e decidi cosa sembra migliore. E non esitate a chiedere!

In particolare: non rompere la retrocompatibilità solo per rispettare questa guida!

Alcuni altri buoni motivi per ignorare una particolare linea guida:

  • L’applicazione della linea guida renderebbe il codice meno leggibile, anche per chi è abituato a leggere il codice che segue questa guida di stile.

  • Per essere coerenti con il codice circostante che non la rispetta (forse per ragioni storiche) - sebbene questa sia anche un’opportunità per ripulire il casino di qualcun altro (in vero stile XP).

  • Perché il codice in questione è stato scritto prima dell’introduzione della linea guida e non c’è nessun altro motivo per modificare quel codice.

  • Quando il codice deve rimanere compatibile con le versioni precedenti di Clojure che non supportano la funzionalità consigliata dalla guida di stile.

Sono disponibili traduzioni della guida originale nelle seguenti lingue:

Note
Queste traduzioni (compresa questa in Italiano) non sono mantenute dal team di editori della versione originale Inglese, quindi la qualità e livello di completezza può variare. Le versioni tradotte della guida spesso ritardano con le aggiunte rispetto all’originale.

Quasi tutti sono convinti che ogni stile tranne il proprio sia brutto e illeggibile. Lascia fuori il "tranne il proprio" e hanno probabilmente ragione…​

— Jerry Coffin (sull' indentazione)

Ove possibile, evita di creare righe più lunghe di 80 caratteri.

Perchè preoccuparsi per 80 caratteri in un mondo con schermi larghissimi?

Molte persone in questi giorni ritengono che una lunghezza massima della riga di 80 caratteri sia solo un residuo del passato e ha poco senso oggi. Dopo tutto - i display moderni possono facilmente contenere oltre 200 caratteri su una singola riga. Eppure ci sono alcuni importanti vantaggi che si ottengono dall’attenersi a righe di codice più brevi.

Innanzitutto, numerosi studi hanno dimostrato che gli esseri umani leggono molto più velocemente verticalmente e linee di testo molto lunghe ostacolano il processo di lettura. Come mostrato in precedenza, uno dei principi di questa guida di stile è ottimizzare il codice che scriviamo per il consumo umano.

Inoltre, la limitazione della larghezza della finestra dell’editor richiesta rende possibile avere diversi file aperti fianco a fianco e funziona bene quando si utilizzano strumenti per la revisione del codice che presentano le due versioni in colonne adiacenti.

Il wrapping predefinito nella maggior parte degli strumenti interrompe la struttura visiva del codice, rendendo più difficile la comprensione. I limiti sono scelti per evitare il wrapping negli editor con la larghezza della finestra impostata su 80, anche se lo strumento posiziona un glifo marcatore nella colonna finale durante il wrapping delle righe. Alcuni strumenti basati sul Web potrebbero non offrire un wrapping dinamico della linea.

Alcuni team preferiscono fortemente una lunghezza della linea più lunga. Per il codice gestito esclusivamente o principalmente da un team che può raggiungere un accordo su questo problema, va bene aumentare il limite di lunghezza della riga fino a 100 caratteri o fino a 120 caratteri. Per favore, frena l’impulso di andare oltre i 120 caratteri.

Usa gli spazi per l’indentazione. Non i tabs.

Usa 2 spazi per indentare i corpi dei form che hanno parametri. Questo include tutte le varianti di def , form speciali e macro che introducono associazioni locali (ad esempio loop, let, when-let) e tante macro come when, cond, as->, cond->, case, with-*, ecc.

;; bene
(when qualcosa
  (qualcos-altro))

(with-out-str
  (println "Ciao, ")
  (println "mondo!"))

;; male - quattro spazi
(when qualcosa
    (qualcos-altro))

;; male - uno spazio
(with-out-str
 (println "Ciao, ")
 (println "mondo!"))

Allinea verticalmente gli argomenti delle funzioni (macro) su più linee.

;; bene
(filter even?
        (range 1 10))

;; male - argomento allineato con il nome della funzione (uno spazio di indentazione)
(filter even?
 (range 1 10))

;; male - due spazi di indentazione
(filter even?
  (range 1 10))

Il ragionamento dietro a questa linea guida è piuttosto semplice - gli argomenti sono più facili da processare per il cervello umano se si distinguono e sono vicini.

Note
Generalmente, dovresti utilizzare la formattazione mostrata nella scorsa linea guida, a meno che tu sia limitato dallo spazio orizzontale disponibile.

Utilizzare un solo spazio di indentazione per gli argomenti della funzione (macro) dove non ci sono argomenti sulla stessa riga del nome della funzione.

;; bene
(filter
 even?
 (range 1 10))

(or
 ala
 bala
 portokala)

;; male - due spazi di indentazione
(filter
  even?
  (range 1 10))

(or
  ala
  bala
  portokala)

Questo può sembrare una strana regola speciale per le persone senza un background di Lisp, ma il ragionamento alla base è abbastanza semplice. Le chiamate di funzione sono nient’altro che normali liste letterali e normalmente quelli sono allineati allo stesso modo di altri tipi di raccolte letterali quando si estendono su più righe:

;; liste letterali
(1
 2
 3)

;; vettori letterali
[1
 2
 3]

;; set (insiemi) letterali
#{1
  2
  3}

Certo, le liste letterali non sono molto comuni in Clojure, ecco perché è comprensibile che per molte persone le liste letterali non sono altro che una sintassi di invocazione.

Come vantaggio collaterale, anche in questo scenario gli argomenti delle funzioni sono ancora allineati, solo che essi sono accidentalmente allineati anche con il nome della funzione.

Indentazione semantica vs Indentazione fissa

Le linee guida per indentare in modo diverso le macro con il form del corpo da tutte le altre chiamate di macro e funzioni sono note collettivamente come "rientro semantico". In poche parole, questo significa che il codice è indentato in modo diverso, in modo che l’indentazione dia al lettore del codice alcuni cenni sul suo significato.

Lo svantaggio di questo approccio è che richiede che i formattatori di codice Clojure siano più intelligenti. Devono cosiì elaborare le liste di argomenti delle "macro" e fare affidamento sul fatto che le persone denominino i propri parametri in modo coerente o elaborino altri metadati di indentazione.

Alcune persone nella comunità di Clojure sostengono che non ne valga la pena e che tutto dovrebbe semplicemente essere indentato nello stesso modo. Ecco alcuni esempi:

;;; Indentazione fissa
;;
;; macro
(when qualcosa
  (qualcos-altro))

(with-out-str
  (println "Ciao, ")
  (println "mondo!"))

;; chiamata di funzione su due linee
(filter even?
  (range 1 10))

;; chiamata di funzione su tre linee
(filter
  even?
  (range 1 10))

Questo suggerimento ha sicuramente guadagnato terreno nella comunità, ma va anche contro gran parte della tradizione Lisp e uno degli obiettivi primari di questa guida di stile -vale a dire ottimizzare il codice per il consumo umano.

C’è un’eccezione alla regola di indentazione fissa: gli elenchi di dati (quelli che non sono un’invocazione di funzione):

;;; Indentazione fissa
;;
;; liste letterali
;; facciamo ancora
(1
 2
 3
 4
 5
 6)

;; e anche
(1 2 3
 4 5 6)

;; invece di
(1 2 3
  4 5 6)

;; oppure
(1
  2
  3
  4
  5
  6)

Ciò garantisce che gli elenchi siano coerenti con il modo in cui gli altri tipi di raccolta sono normalmente indentati.

Allinea verticalmente le associazioni 'let' (e simili a 'let').

;; bene
(let [cosa1 "delle cose"
      cosa2 "altre cose"]
  (foo cosa1 cosa2))

;; male
(let [cosa1 "delle cose"
  cosa2 "altre cose"]
  (foo cosa1 cosa2))

Allinea verticalmente le chiavi delle mappe.

;; bene
{:cosa1 cosa1
 :cosa2 cosa2}

;; male
{:cosa1 cosa1
:cosa2 cosa2}

;; male
{:cosa1 cosa1
  :cosa2 cosa2}

Usa il fine linea in stile Unix.[5]

Tip

Se stai usando Git potresti voler aggiungere la seguente impostazione di configurazione per proteggere il progetto dai fine linea di Windows che si insinuano al suo interno:

$ git config --global core.autocrlf true

Termina ogni file andando a capo.

Tip
Questo dovrebbe essere fatto per configurazione dell’editor, non manualmente.

Se del testo precede una parentesi di apertura ((, { e [) o segue una parentesi di chiusura (), } e ]), separa il testo da quella parentesi con uno spazio. Al contrario, non lasciare spazio dopo una parentesi di apertura e prima del testo seguente o dopo il testo precedente e prima di una parentesi di chiusura.

;; bene
(foo (bar baz) quux)

;; male
(foo(bar baz)quux)
(foo ( bar baz ) quux)

Lo zucchero sintattico provoca il cancro al punto e virgola.

— Alan Perlis

Non utilizzare le virgole tra gli elementi dei valori letterali di raccolta sequenziali.

;; bene
[1 2 3]
(1 2 3)

;; male
[1, 2, 3]
(1, 2, 3)

Prendi in considerazione il miglioramento della leggibilità dei valori letterali della mappa tramite un uso giudizioso di virgole e interruzioni di riga.

;; bene
{:name "Bruce Wayne" :alter-ego "Batman"}

;; buono e probabilmente un po 'più leggibile
{:name "Bruce Wayne"
 :alter-ego "Batman"}

;; buono e probabilmente un po 'più compatto
{:name "Bruce Wayne", :alter-ego "Batman"}

Posiziona tutte le parentesi finali su un’unica riga anziché su righe distinte.

;; bene; linea singola
(when something
  (something-else))

;; male; linee separate
(when something
  (something-else)
)

Inserisci una singola linea vuota in mezzo ai form del top-level.

;; bene
(def x ...)

(defn foo ...)

;; male
(def x ...)
(defn foo ...)

;; male
(def x ...)


(defn foo ...)

Un eccezione alla regola è il raggruppamento di definizioni (def).

;; bene
(def min-rows 10)
(def max-rows 20)
(def min-cols 15)
(def max-cols 30)

Non inserire righe vuote nel mezzo di una funzione o macro. È possibile fare un’eccezione per indicare il raggruppamento di costrutti a coppie come si trovano ad es. let e cond, nel caso non ci stiano sulla stessa linea.

;; bene
(defn fibo-iter
  ([n] (fibo-iter 0 1 n))
  ([curr nxt n]
   (cond
     (zero? n) curr
     :else (recur nxt (+' curr nxt) (dec n)))))

;; accettabile - la linea vuota delimita la coppia del costrutto cond
(defn fibo-iter
  ([n] (fibo-iter 0 1 n))
  ([curr nxt n]
   (cond
     (zero? n)
     curr

     :else
     (recur nxt (+' curr nxt) (dec n)))))

;; male
(defn fibo-iter
  ([n] (fibo-iter 0 1 n))

  ([curr nxt n]
   (cond
     (zero? n) curr

     :else (recur nxt (+' curr nxt) (dec n)))))

Occasionalmente, potrebbe sembrare una buona idea aggiungere una riga vuota qua e là in una definizione di funzione più lunga, ma se arrivi a questo punto dovresti anche considerare se questa lunga funzione non stia facendo troppo e potrebbe potenzialmente essere suddivisa in più funzioni.

Evita di lasciare spazi a fine linea.

Usa un file per namespace e un namespace per file.

;; bene
(ns foo.bar)

;; male
(ns foo.bar)
(ns baz.qux)

;; male
(in-ns quux.quuz)
(in-ns quuz.corge)

;; male
(ns foo.bar) o (in-ns foo.bar) in più file

Evita i namespace con un singolo segmento.

;; bene
(ns esempio.ns)

;; male
(ns esempio)

Gli spazi dei nomi esistono per eliminare ambiguità tra i nomi. Utilizzare uno spazio dei nomi formato da un singolo segmento ti mette in conflitto diretto con tutti gli altri che usano spazi dei nomi con un singolo segmento, rendendo così più probabile il conflitto con un’altra base di codice.

In pratica ciò significa che le biblioteche non dovrebbero mai utilizzare spazi dei nomi a segmento singolo per evitare conflitti di namespace con altre librerie. All’interno della tua app privata, ovviamente, puoi fare quello che vuoi.

Tip
è prassi comune utilizzare la convenzione dominio.nome-libreria o nome-libreria.core per le librerie con un singolo spazio dei nomi al loro interno. Continua a leggere per una maggiore copertura dell’argomento della denominazione dello spazio dei nomi.

Ci sono altre ragioni per le quali potresti voler evitare spazi dei nomi a segmento singolo, quindi dovresti pensarci bene prima di usarli nel tuo codice.

Evita di usare spazi dei nomi esageratamente lunghi (per esempio, più di 5 segmenti).

Inizia ogni spazio dei nomi con un form ns completo, composto da refer, require e import, convenzionalmente in questo ordine.

(ns examples.ns
  (:refer-clojure :exclude [next replace remove])
  (:require [clojure.string :as s :refer [blank?]])
  (:import java.util.Date))

Quando ci sono più dipendenze, potresti voler assegnare a ciascuna la propria riga. Ciò facilita l’ordinamento, la leggibilità e differenze più pulite per le modifiche alle dipendenze.

;; meglio
(ns examples.ns
  (: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]))

;; bene
(ns examples.ns
  (: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]))

;; male
(ns examples.ns
  (: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 ns preferisci :require :as piuttosto che :require :refer e piuttosto che :require :refer :all. Preferisci :require piuttosto :use; l’ultima forma dovrebbe essere considerata deprecata per codice nuovo.

;; bene
(ns examples.ns
  (:require [clojure.zip :as zip]))

;; bene
(ns examples.ns
  (:require [clojure.zip :refer [lefts rights]]))

;; accettabile
(ns examples.ns
  (:require [clojure.zip :refer :all]))

;; male
(ns examples.ns
  (:use clojure.zip))

In ns , ordina i requisiti e le importazioni. Questo facilità la leggibilità e evita la duplicazione, specialmente quando la lista degli spazi dei nomi richiesti / importati è molto lunga.

;; bene
(ns examples.ns
  (:require
   [baz.core :as baz]
   [clojure.java.shell :as sh]
   [clojure.set :as set]
   [clojure.string :as s :refer [blank?]]
   [foo.bar :as foo]))

;; male
(ns examples.ns
  (:require
   [clojure.string :as s :refer [blank?]]
   [clojure.set :as set]
   [baz.core :as baz]
   [foo.bar :as foo]
   [clojure.java.shell :as sh]))

Molti spazi dei nomi di Clojure hanno alias idiomatici che sei incoraggiato a utilizzare all’interno dei tuoi progetti - ad es. il modo più comune per rechiedere clojure.string è: [clojure.string :as str].

Note
Potrebbe dare l’impressione di mascherare clojure.core.str, ma non lo fa. Ci si aspetta che clojure.core/str e clojure.string/* vengano usati in un namespace come str e str/qualcosa senza conflitti.
;; bene
(ns ... (:require [clojure.string :as str] ...))

(str/join ...)

;; meno bene - sii idiomatico e usa `str/`
(ns ... (:require [clojure.string :as string] ...))

(string/join ...)

Come notato nella sezione successiva, è generalmente considerato idiomatico usare un alias che è l’ultimo segmento dello spazio dei nomi, se questo lo rende unico, oppure i due segmenti, in genere eliminando parti ridondanti come "clj" o "core".

Tra gli spazi dei nomi core e Contrib di Clojure, i seguenti spazi dei nomi hanno alias idiomatici che seguono questo schema:

Namespace

Alias idiomatico

clojure.datafy

datafy

clojure.edn

edn

clojure.java.io

io

clojure.math

math

clojure.set

set

clojure.walk

walk

clojure.zip

zip

clojure.core.async

async

clojure.data.csv

csv

clojure.data.xml

xml

clojure.tools.cli

cli

Poi ci sono alcuni spazi dei nomi core e Contrib che hanno alias idiomatici più brevi:

Namespace

Alias idiomatico

clojure.java.shell

sh

clojure.pprint

pp

clojure.spec.alpha

s

clojure.string

str

clojure.core.matrix

mat

clojure.tools.logging

log

clojure.core.protocols

p

clojure.core.reducers

r

E tra le librerie della community comunemente usate, ce ne sono anche molte che hanno alias idiomatici ampiamente utilizzati per diversi spazi dei nomi:

Namespace

Alias idiomatico

cheshire.core

json

clj-yaml.core

yaml

clj-http.client

http

hugsql.core

sql

java-time

time

next.jdbc

jdbc

Sopra abbiamo coperto una manciata di spazi dei nomi popolari e i loro alias idiomatici. Potresti aver notato che questi sono un po' incoerenti:

  • clojure.string diventa str

  • clojure.pprint diventa pp

  • clojure.walk diventa walk

  • clojure.spec.alpha diventa s

È chiaro che l’unica cosa che hanno in comune è che mirano a essere concisi, ma hanno comunque un significato (dare w come alias a clojure.walk sarebbe stato conciso, ma non avrebbe molto significato).

Ma cosa fare con tutti gli altri spazi dei nomi là fuori che non hanno alias idiomatici? Beh, faresti meglio a essere coerente nel tuo approccio alla derivazione degli alias per loro, altrimenti le persone che lavorano su una base di codice Clojure condivisa sperimenteranno molta confusione. Ecco alcune regole che dovresti seguire.[6]

1) Rendi l’alias uguale al nome dello spazio dei nomi con le parti iniziali rimosse.

(ns com.example.application
  (:require
   [clojure.java.io :as io]
   [clojure.reflect :as reflect]))

2) Mantieni un numero sufficiente di parti finali per rendere unico ogni alias.

[clojure.data.xml :as data.xml]
[clojure.xml :as xml]
Tip
Sì, gli alias dello spazio dei nomi possono contenere dei punti. Fatene buon uso.

3) Elimina parole ridondanti come "core" e "clj" negli alias.

[clj-time.core :as time]
[clj-time.format :as time.format]

In un progetto, è bene essere coerenti con gli alias dello spazio dei nomi; ad esempio, non richiedere clojure.string come str in uno spazio dei nomi ma string in un altro. Se segui le due linee guida precedenti sei sostanzialmente coperto, ma se opti per lo schema di aliasing dello spazio dei nomi personalizzato è comunque importante applicarlo coerentemente all’interno dei vostri progetti.

Le uniche vere difficoltà nella programmazione sono l’invalidazione della cache e dare nomi alle cose.

— Phil Karlton

Quando stai dando un nome ad un namespace preferisci i seguenti due schemi:

  • project.module

  • organization.project.module

Quando segui lo schema di denominazione project.module e il tuo progetto ha un singolo spazio dei nomi (di implementazione) è comune chiamarlo project.core. Evita il nome project.core in tutti gli altri casi, come nomi più informativi sono sempre un’idea migliore.

Usa lisp-case nei segmenti di namespace compositi (ad esempio bruce.project-euler).

Note
Molte comunità di programmatori non Lisp si riferiscono a lisp-case come kebab-case, ma sappiamo tutti che Lisp esisteva molto prima del kebab.

Usa lisp-case per i nomi delle funzioni e variabili.

;; bene
(def some-var ...)
(defn some-fun ...)

;; male
(def someVar ...)
(defn somefun ...)
(def some_fun ...)

Usa CapitalCase per i protocolli, records, structs, e i tipi. (mantieni acronimi come HTTP, RFC, XML maiuscoli.)

Note
CapitalCase è anche conosciuto come UpperCamelCase, `CapitalWords e PascalCase.

I nomi dei metodi predicati (metodi che restituiscono un valore booleano) dovrebbe terminare con un punto interrogativo (ad esempio, "pari?").

;; bene
(defn palindrome? ...)

;; male
(defn palindrome-p ...) ; in stile Common Lisp
(defn is-palindrome ...) ; in stile Java

I nomi delle funzioni/macro che non sono sicure nelle transazioni STM dovrebbero terminare con un punto esclamativo (ad es. reset!).

Usa "->" invece di "to" nei nomi delle funzioni di conversione.

;; bene
(defn f->c ...)

;; non così bene
(defn f-to-c ...)

Usa earmuffs per cose destinate a essere riassociate (ad es. sono dinamiche).

;; bene
(def ^:dynamic *a* 10)

;; male
(def ^:dynamic a 10)

Non usare una notazione speciale per le costanti; tutto è presunto una costante se non diversamente specificato.

;; bene
(def max-size 10)

;; male
(def MAX-SIZE 10) ; in stile Java
(def +max-size+ 10) ; in stile Common Lisp , costante globale
(def *max-size* 10) ; in stile Common Lisp , variabile globale
Note
Notoriamente *clojure-version* sfida questa convenzione, ma dovresti trattare questa scelta di denominazione come una stranezza storica e non come un esempio da seguire.

Usa _ per destrutturare obiettivi e nomi di argomenti formali di cui il valore verrà ignorato dal codice a portata di mano.

;; bene
(let [[a b _ c] [1 2 3 4]]
  (println a b c))

(dotimes [_ 3]
  (println "Hello!"))

;; male
(let [[a b c d] [1 2 3 4]]
  (println a b d))

(dotimes [i 3]
  (println "Hello!"))

Tuttavia, quando può aiutare la comprensione del tuo codice, può essere utile nominare esplicitamente argomenti o mappe inutilizzati da cui stai destrutturando. In questo caso, anteponi al nome un carattere di sottolineatura per segnalare esplicitamente che la variabile dovrebbe essere inutilizzata.

;; bene
(defn myfun1 [context _]
 (assoc context :foo "bar"))

(defn myfun2 [context {:keys [id]}]
 (assoc context :user-id id))

;; meglio ancora
(defn myfun1 [context _user]
 (assoc context :foo "bar"))

(defn myfun2 [context {:keys [id] :as _user}]
 (assoc context :user-id id))

Segui l’esempio di clojure.core per nomi idiomatici come pred e coll.

  • nelle funzioni:

    • f, g, h - input delle funzioni

    • n - input intero, di solito indica grandezza

    • index, i - intero usato per indici

    • x, y - numeri

    • xs - sequenza

    • m - mappa

    • k, ks - chiave, chiavi

    • v, vs - valore, valori (come in una coppia chiave/valore)

    • s - input stringa

    • re - regular expression

    • sym - simbolo

    • coll - una collezione

    • pred - una closure predicativa

    • & more - input variadico

    • xf - xform, un trasduttore

    • ns - namespace[7]

  • nelle macro:

    • expr - un espressione

    • body - il corpo di una macro

    • binding - un vettore di legame macro

  • nei metodi (quando specificato in defprotocol, deftype, defrecord, reify, etc):

    • this - per il primo argomento, indicando un riferimento all’oggetto - o in alternativa, un nome coerente che descrive l’oggetto

Facoltativamente omettere la nuova riga tra il nome della funzione e l’argomento vettore per defn quando non c’è docstring.

;; bene
(defn foo
  [x]
  (bar x))

;; bene
(defn foo [x]
  (bar x))

;; male
(defn foo
  [x] (bar x))

Posiziona il dispatch-val di un multimetodo sulla stessa riga del nome della funzione.

;; bene
(defmethod foo :bar [x] (baz x))

(defmethod foo :bar
  [x]
  (baz x))

;; male
(defmethod foo
  :bar
  [x]
  (baz x))

(defmethod foo
  :bar [x]
  (baz x))

Facoltativamente, ometti la nuova riga tra il vettore degli argomenti e un corpo della funzione molto corto.

;; bene
(defn foo [x]
  (bar x))

;; bene per un corpo funzione corto
(defn foo [x] (bar x))

;; bene per funzioni con multi-arità
(defn foo
  ([x] (bar x))
  ([x y]
   (if (predicate? x)
     (bar x)
     (baz x))))

;; male
(defn foo
  [x] (if (predicate? x)
        (bar x)
        (baz x)))

Indenta ogni form di arità di una definizione di funzione allineata verticalmente con i suoi parametri.

;; bene
(defn foo
  "I have two arities."
  ([x]
   (foo x 1))
  ([x y]
   (+ x y)))

;; male - indentazione extra
(defn foo
  "I have two arities."
  ([x]
    (foo x 1))
  ([x y]
    (+ x y)))

Ordina le arità di una funzione dal minor numero al maggior numero di argomenti. Il caso comune di funzioni con arità multipla è che alcuni argomenti K specificano completamente il comportamento della funzione, e che le arità N < K applicano parzialmente l’arità K, e le aritò N > K forniscono una piegatura dell’arità K su varargs.

;; bene - è facile cercare l'n-esima arità
(defn foo
  "I have two arities."
  ([x]
   (foo x 1))
  ([x y]
   (+ x y)))

;; okay - le altre arità sono applicazioni della prima arità
(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)))

;; male - disordinata senza motivo
(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)))

Evita funzioni più lunghe di 10 LOC (righe di codice). Idealmente, la maggior parte delle funzioni saranno più brevi di 5 LOC.

Avoid parameter lists with more than three or four positional parameters.

Prefer function pre and post conditions to checks inside a function’s body.

;; good
(defn foo [x]
  {:pre [(pos? x)]}
  (bar x))

;; bad
(defn foo [x]
  (if (pos? x)
    (bar x)
    (throw (IllegalArgumentException. "x must be a positive number!")))

Avoid the use of namespace-manipulating functions like require and refer. They are entirely unnecessary outside of a REPL environment.

Avoid forward references. They are occasionally necessary, but such occasions are rare in practice.

Use declare to enable forward references when forward references are necessary.

Prefer higher-order functions like map to loop/recur.

Don’t define vars inside functions.

;; very bad
(defn foo []
  (def x 5)
  ...)

Don’t shadow clojure.core names with local bindings.

;; bad - clojure.core/map must be fully qualified inside the function
(defn foo [map]
  ...)

Use alter-var-root instead of def to change the value of a var.

;; good
(def thing 1) ; value of thing is now 1
; do some stuff with thing
(alter-var-root #'thing (constantly nil)) ; value of thing is now nil

;; bad
(def thing 1)
; do some stuff with thing
(def thing nil)
; value of thing is now nil

Use seq as a terminating condition to test whether a sequence is empty (this technique is sometimes called nil punning).

;; good
(defn print-seq [s]
  (when (seq s)
    (prn (first s))
    (recur (rest s))))

;; bad
(defn print-seq [s]
  (when-not (empty? s)
    (prn (first s))
    (recur (rest s))))

Prefer vec over into when you need to convert a sequence into a vector.

;; good
(vec some-seq)

;; bad
(into [] some-seq)

Use the boolean function if you need to convert something to an actual boolean value (true or false).

;; good
(boolean (foo bar))

;; bad
(if (foo bar) true false)
Note
Don’t forget that the only values in Clojure that are "falsey" are false and nil. Everything else will evaluate to true when passed to the boolean function.

You’ll rarely need an actual boolean value in Clojure, but it’s useful to know how to obtain one when you do.

Use when instead of if with just the truthy branch, as in (if condition (something…​)) or (if …​ (do …​)).

;; good
(when pred
  (foo)
  (bar))

;; bad
(if pred
  (do
    (foo)
    (bar)))

Use if-let instead of let + if.

;; good
(if-let [result (foo x)]
  (something-with result)
  (something-else))

;; bad
(let [result (foo x)]
  (if result
    (something-with result)
    (something-else)))

Use when-let instead of let + when.

;; good
(when-let [result (foo x)]
  (do-something-with result)
  (do-something-more-with result))

;; bad
(let [result (foo x)]
  (when result
    (do-something-with result)
    (do-something-more-with result)))

Use if-not instead of (if (not …​) …​).

;; good
(if-not pred
  (foo))

;; bad
(if (not pred)
  (foo))

Use when-not instead of (when (not …​) …​).

;; good
(when-not pred
  (foo)
  (bar))

;; bad
(when (not pred)
  (foo)
  (bar))

Use when-not instead of (if-not …​ (do …​)).

;; good
(when-not pred
  (foo)
  (bar))

;; bad
(if-not pred
  (do
    (foo)
    (bar)))

Use not= instead of (not (= …​)).

;; good
(not= foo bar)

;; bad
(not (= foo bar))

Prefer printf over (print (format …​)).

;; good
(printf "Hello, %s!\n" name)

;; ok
(println (format "Hello, %s!" name))

When doing comparisons, leverage the fact that Clojure’s functions <, >, etc. accept a variable number of arguments.

;; good
(< 5 x 10)

;; bad
(and (> x 5) (< x 10))

Prefer % over %1 in function literals with only one parameter.

;; good
#(Math/round %)

;; bad
#(Math/round %1)

Prefer %1 over % in function literals with more than one parameter.

;; good
#(Math/pow %1 %2)

;; bad
#(Math/pow % %2)

Don’t wrap functions in anonymous functions when you don’t need to.

;; good
(filter even? (range 1 10))

;; bad
(filter #(even? %) (range 1 10))

Don’t use function literals if the function body will consist of more than one form.

;; good
(fn [x]
  (println x)
  (* x 2))

;; bad (you need an explicit do form)
#(do (println %)
     (* % 2))

Prefer anonymous functions over complement, comp and partial, as this results in simpler code most of the time.[8]

;; good
(filter #(not (some-pred? %)) coll)

;; okish
(filter (complement some-pred?) coll)
;; Assuming `(:require [clojure.string :as str])`...

;; good
(map #(str/capitalize (str/trim %)) ["top " " test "])

;; okish
(map (comp str/capitalize str/trim) ["top " " test "])

comp is quite useful when composing transducer chains, though.

;; good
(def xf
  (comp
    (filter odd?)
    (map inc)
    (take 5)))
;; good
(map #(+ 5 %) (range 1 10))

;; okish
(map (partial + 5) (range 1 10))

Prefer the use of the threading macros -> (thread-first) and ->> (thread-last) to heavy form nesting.

;; good
(-> [1 2 3]
    reverse
    (conj 4)
    prn)

;; not as good
(prn (conj (reverse [1 2 3])
           4))

;; good
(->> (range 1 10)
     (filter even?)
     (map (partial * 2)))

;; not as good
(map (partial * 2)
     (filter even? (range 1 10)))

Parentheses are not required when using the threading macros for functions having no argument specified, so use them only when necessary.

;; good
(-> x fizz :foo first frob)

;; bad; parens add clutter and are not needed
(-> x (fizz) (:foo) (first) (frob))

;; good, parens are necessary with an arg
(-> x
    (fizz a b)
    :foo
    first
    (frob x y))

The arguments to the threading macros -> (thread-first) and ->> (thread-last) should line up.

;; good
(->> (range)
     (filter even?)
     (take 5))

;; bad
(->> (range)
  (filter even?)
  (take 5))

Use :else as the catch-all test expression in cond.

;; good
(cond
  (neg? n) "negative"
  (pos? n) "positive"
  :else "zero")

;; bad
(cond
  (neg? n) "negative"
  (pos? n) "positive"
  true "zero")

Prefer condp instead of cond when the predicate & expression don’t change.

;; good
(cond
  (= x 10) :ten
  (= x 20) :twenty
  (= x 30) :thirty
  :else :dunno)

;; much better
(condp = x
  10 :ten
  20 :twenty
  30 :thirty
  :dunno)

Prefer case instead of cond or condp when test expressions are compile-time constants.

;; good
(cond
  (= x 10) :ten
  (= x 20) :twenty
  (= x 30) :forty
  :else :dunno)

;; better
(condp = x
  10 :ten
  20 :twenty
  30 :forty
  :dunno)

;; best
(case x
  10 :ten
  20 :twenty
  30 :forty
  :dunno)

Use short forms in cond and related. If not possible give visual hints for the pairwise grouping with comments or empty lines.

;; good
(cond
  (test1) (action1)
  (test2) (action2)
  :else   (default-action))

;; ok-ish
(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)))

Use a set as a predicate when appropriate.

;; good
(remove #{1} [0 1 2 3 4 5])

;; bad
(remove #(= % 1) [0 1 2 3 4 5])

;; good
(count (filter #{\a \e \i \o \u} "mary had a little lamb"))

;; bad
(count (filter #(or (= % \a)
                    (= % \e)
                    (= % \i)
                    (= % \o)
                    (= % \u))
               "mary had a little lamb"))

Use (inc x) & (dec x) instead of (+ x 1) and (- x 1).

Use (pos? x), (neg? x) & (zero? x) instead of (> x 0), (< x 0) & (= x 0).

Use list* instead of a series of nested cons invocations.

;; good
(list* 1 2 3 [4 5])

;; bad
(cons 1 (cons 2 (cons 3 [4 5])))

Use the sugared Java interop forms.

;;; object creation
;; good
(java.util.ArrayList. 100)

;; bad
(new java.util.ArrayList 100)

;;; static method invocation
;; good
(Math/pow 2 10)

;; bad
(. Math pow 2 10)

;;; instance method invocation
;; good
(.substring "hello" 1 3)

;; bad
(. "hello" substring 1 3)

;;; static field access
;; good
Integer/MAX_VALUE

;; bad
(. Integer MAX_VALUE)

;;; instance field access
;; good
(.someField some-object)

;; bad
(. some-object someField)

Use the compact metadata notation for metadata that contains only slots whose keys are keywords and whose value is boolean true.

;; good
(def ^:private a 5)

;; bad
(def ^{:private true} a 5)

Denote private parts of your code.

;; good
(defn- private-fun [] ...)

(def ^:private private-var ...)

;; bad
(defn private-fun [] ...) ; not private at all

(defn ^:private private-fun [] ...) ; overly verbose

(def private-var ...) ; not private at all

To access a private var (e.g. for testing), use the @#'some.ns/var form.

Be careful regarding what exactly you attach metadata to.

;; we attach the metadata to the var referenced by `a`
(def ^:private a {})
(meta a) ;=> nil
(meta #'a) ;=> {:private true}

;; we attach the metadata to the empty hash-map value
(def a ^:private {})
(meta a) ;=> {:private true}
(meta #'a) ;=> nil

It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures.

— Alan J. Perlis

Avoid the use of lists for generic data storage (unless a list is exactly what you need).

Prefer the use of keywords for hash keys.

;; good
{:name "Bruce" :age 30}

;; bad
{"name" "Bruce" "age" 30}

Prefer the use of the literal collection syntax where applicable. However, when defining sets, only use literal syntax when the values are compile-time constants.

;; good
[1 2 3]
#{1 2 3}
(hash-set (func1) (func2)) ; values determined at runtime

;; bad
(vector 1 2 3)
(hash-set 1 2 3)
#{(func1) (func2)} ; will throw runtime exception if (func1) = (func2)

Avoid accessing collection members by index whenever possible.

Prefer the use of keywords as functions for retrieving values from maps, where applicable.

(def m {:name "Bruce" :age 30})

;; good
(:name m)

;; more verbose than necessary
(get m :name)

;; bad - susceptible to NullPointerException
(m :name)

Leverage the fact that most collections are functions of their elements.

;; good
(filter #{\a \e \o \i \u} "this is a test")

;; bad - too ugly to share

Leverage the fact that keywords can be used as functions of a collection.

((juxt :a :b) {:a "ala" :b "bala"})

Avoid the use of transient collections, except for performance-critical portions of the code.

Avoid the use of Java collections.

Avoid the use of Java arrays, except for interop scenarios and performance-critical code dealing heavily with primitive types.

Don’t use the interop syntax to construct type and record instances. deftype and defrecord automatically create constructor functions. Use those instead of the interop syntax, as they make it clear that you’re dealing with a deftype or a defrecord. See this article for more details.

(defrecord Foo [a b])
(deftype Bar [a b])

;; good
(->Foo 1 2)
(map->Foo {:b 4 :a 3})
(->Bar 1 2)

;; bad
(Foo. 1 2)
(Bar. 1 2)

Note that deftype doesn’t define the map->Type constructor. It’s available only for records.

Add custom type/record constructors when needed (e.g. to validate properties on record creation). See this article for more details.

(defrecord Customer [id name phone email])

(defn make-customer
  "Creates a new customer record."
  [{:keys [name phone email]}]
  {:pre [(string? name)
         (valid-phone? phone)
         (valid-email? email)]}
  (->Customer (next-id) name phone email))

Feel free to adopt whatever naming convention or structure you’d like for such custom constructors.

Don’t override the auto-generated type/record constructor functions. People expect them to have a certain behaviour and changing this behaviour violates the principle of least surprise. See this article for more details.

(defrecord Foo [num])

;; good
(defn make-foo
  [num]
  {:pre [(pos? num)]}
  (->Foo num))

;; bad
(defn ->Foo
  [num]
  {:pre [(pos? num)]}
  (Foo. num))

Consider wrapping all I/O calls with the io! macro to avoid nasty surprises if you accidentally end up calling such code in a transaction.

Avoid the use of ref-set whenever possible.

(def r (ref 0))

;; good
(dosync (alter r + 5))

;; bad
(dosync (ref-set r 5))

Try to keep the size of transactions (the amount of work encapsulated in them) as small as possible.

Avoid having both short- and long-running transactions interacting with the same Ref.

Use send only for actions that are CPU bound and don’t block on I/O or other threads.

Use send-off for actions that might block, sleep, or otherwise tie up the thread.

Avoid atom updates inside STM transactions.

Try to use swap! rather than reset!, where possible.

(def a (atom 0))

;; good
(swap! a + 5)

;; not as good
(reset! a 5)

Prefer math functions from clojure.math over (Java) interop or rolling your own.

;; good
(clojure.math/pow 2 5)

;; okish
(Math/pow 2 5)

The JDK package java.lang.Math provides access to many useful math functions. Prior to version 1.11, Clojure relied on using these via interop, but this had issues with discoverability, primitive performance, higher order application, and portability. The new clojure.math namespace provides wrapper functions for the methods available in java.lang.Math for long and double overloads with fast primitive invocation.

Prefer string manipulation functions from clojure.string over Java interop or rolling your own.

;; good
(clojure.string/upper-case "bruce")

;; bad
(.toUpperCase "bruce")
Note
Several new functions were added to clojure.string in Clojure 1.8 (index-of, last-index-of, starts-with?, ends-with? and includes?). You should avoid using those if you need to support older Clojure releases.

Reuse existing exception types. Idiomatic Clojure code — when it does throw an exception — throws an exception of a standard type (e.g. java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException, java.lang.IllegalStateException, java.io.IOException).

Favor with-open over finally.

Don’t write a macro if a function will do.

Create an example of a macro usage first and the macro afterwards.

Break complicated macros into smaller functions whenever possible.

A macro should usually just provide syntactic sugar and the core of the macro should be a plain function. Doing so will improve composability.

Prefer syntax-quoted forms over building lists manually.

In this section we’ll go over some common metadata for namespaces and vars that Clojure development tools can leverage.

The most common way to document when a public API was added to a library is via the :added metadata.

(def ^{:added "0.5"} foo
  42)

(ns foo.bar
  "A very useful ns."
  {:added "0.8"})

(defn ^{:added "0.5"} foo
  (bar))
Tip
If you’re into SemVer, it’s a good idea to omit the patch version. This means you should use 0.5 instead of 0.5.0. This applies for all metadata data that’s version related.

The most common way to document when a public API was changed in a library is via the :changed metadata. This metadata makes sense only for vars and you should be using it sparingly, as changing the behavior of a public API is generally a bad idea.

Still, if you decide to do it, it’s best to make that clear to the API users.

(def ^{:added "0.5"
       :changed "0.6"} foo
  43)

The most common way to mark deprecated public APIs is via the :deprecated metadata. Normally you’d use as the value the version in which something was deprecated in case of versioned software (e.g. a library) or simply true in the case of unversioned software (e.g. some web application).

;;; good
;;
;; in case we have a version
(def ^{:deprecated "0.5"} foo
  "Use `bar` instead."
  42)

(ns foo.bar
  "A deprecated ns."
  {:deprecated "0.8"})

(defn ^{:deprecated "0.5"} foo
  (bar))

;; otherwise
(defn ^:deprecated foo
  (bar))

;;; bad
;;
;; using the docstring to signal deprecation
(def foo
  "DEPRECATED: Use `bar` instead."
  42)

(ns foo.bar
  "DEPRECATED: A deprecated ns.")

Often you’d combine :deprecated with :superseded-by, as there would be some newer API that supersedes whatever got deprecated.

Typically for vars you’ll use a non-qualified name if the replacement lives in the same namespace, and a fully-qualified name otherwise.

;; in case we have a version
(def ^{:deprecated "0.5"
       :superseded-by "bar"} foo
  "Use `bar` instead."
  42)

(ns foo.bar
  "A deprecated ns."
  {:deprecated "0.8"
   :superseded-by "foo.baz"})

(defn ^{:deprecated "0.5"
        :superseded-by "bar"} foo
  (bar))

;; otherwise
(defn ^{:deprecated true
        :superseded-by "bar"} foo
  (bar))
Tip
You can also consider adding :supersedes metadata to the newer APIs, basically the inverse of :superseded-by.

From time to time you might want to point out some related vars/namespaces that the users of your library might be interested in. The most common way to do so would be via the :see-also metadata, which takes a vector of related items. When talking about vars - items in the same namespace don’t need to fully qualified.

;; refers to vars in the same ns
(def ^{:see-also ["bar" "baz"]} foo
  "A very useful var."
  42)

;; refers to vars in some other ns
(defn ^{:see-also ["top.bar" "top.baz"]} foo
  (bar))
Note
Many Clojure programming tools will also try to extract references to other vars from the docstring, but it’s both simpler and more explicit to use the :see-also metadata instead.

Documentation tools like Codox like cljdoc recognize :no-doc metadata. When a var or a namespace has :no-doc metadata, it indicates to these tools that it should be excluded from generated API docs.

To exclude an entire namespace from API docs:

(ns ^:no-doc my-library.impl
  "Internal implementation details")

...

To exclude vars within a documented namespace:

(ns my-library.api)

;; private functions do not get documented
(defn- clearly-private []
  ...)

;; nor do public functions with :no-doc metadata
(defn ^:no-doc shared-helper []
  ...)

;; this function will be documented
(defn api-fn1
  "I am useful to the public"
  []
  ...)

Unlike other Lisp dialects, Clojure doesn’t have a standard metadata format to specify the indentation of macros. CIDER proposed a tool-agnostic indentation specification based on metadata in 2015.[9] Here’s a simple example:

;; refers to vars in the same ns
(defmacro with-in-str
  "[DOCSTRING]"
  {:style/indent 1}
  [s & body]
  ...cut for brevity...)

This instructs the indentation engine that this is a macro with one ordinary parameter and a body after it.

;; without metadata (indented as a regular function)
(dop-iin-str some-string
             foo
             bar
             baz)

;; with metadata (indented as macro with one special param and a body)
(with-in-str some-string
  foo
  bar
  baz)

Unfortunately, as of 2020 there’s still no widespread adoption of :style/indent and many editors and IDEs would just hardcode the indentation rules for common macros.

Note
This approach to indentation ("semantic indentation") is a contested topic in the Clojure community, due to the need for the additional metadata and tooling support. Despite the long tradition of that approach in the Lisp community in general, some people argue to just stop treating functions and macros differently and simply indent everything with a fixed indentation. This article is one popular presentation of that alternative approach.

Good code is its own best documentation. As you’re about to add a comment, ask yourself, "How can I improve the code so that this comment isn’t needed?" Improve the code and then document it to make it even clearer.

— Steve McConnell

Endeavor to make your code as self-explanatory as possible. If you fail to achieve this follow the rest of the guidelines in this section.

Write heading comments with at least four semicolons. Those typically serve to outline/separate major section of code, or to describe important ideas. Often you’d have a section comment followed by a bunch of top-level comments.

;;;; Section Comment/Heading

;;; Foo...
;;; Bar...
;;; Baz...

Write top-level comments with three semicolons.

;;; I'm a top-level comment.
;;; I live outside any definition.

(defn foo [])
Note
While the classic Lisp tradition dictates the use of ;;; for top-level comments, you’ll find plenty of Clojure code in the wild that’s using ;; or even ;.

Write comments on a particular fragment of code before that fragment and aligned with it, using two semicolons.

(defn foo [x]
  ;; I'm a line/code fragment comment.
  x)
Note
While the classic Lisp tradition dictates the use of ;; for line comments, you’ll find plenty of Clojure code in the wild that’s using only ;.

Write margin comments with one semicolon.

(defn foo [x]
  x ; I'm a line/code fragment comment.
  )

Avoid using those in situations that would result in hanging closing parentheses.

Always have at least one space between the semicolon and the text that follows it.

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

Comments longer than a word begin with a capital letter and use punctuation. Separate sentences with one space.

;; This is a good comment.

;; this is a bad comment

Obviously punctuation is not the most important thing about a comment, but a bit of extra effort results in better experience for the readers of our comments.

Avoid superfluous comments.

;; bad
(inc counter) ; increments counter by one

Keep existing comments up-to-date. An outdated comment is worse than no comment at all.

Prefer the use of the #_ reader macro over a regular comment when you need to comment out a particular form.

;; good
(+ foo #_(bar x) delta)

;; bad
(+ foo
   ;; (bar x)
   delta)

Good code is like a good joke - it needs no explanation.

— Russ Olsen

Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory. ("Do, or do not. There is no try." --Yoda)

Annotations should usually be written on the line immediately above the relevant code.

;; good
(defn some-fun
  []
  ;; FIXME: Replace baz with the newer bar.
  (baz))

;; bad
;; FIXME: Replace baz with the newer bar.
(defn some-fun
  []
  (baz))

The annotation keyword is followed by a colon and a space, then a note describing the problem.

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

If multiple lines are required to describe the problem, subsequent lines should be indented as much as the first one.

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

Tag the annotation with your initials and a date so its relevance can be easily verified.

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

In cases where the problem is so obvious that any documentation would be redundant, annotations may be left at the end of the offending line with no note. This usage should be the exception and not the rule.

(defn bar
  []
  (sleep 100)) ; OPTIMIZE

Use TODO to note missing features or functionality that should be added at a later date.

Use FIXME to note broken code that needs to be fixed.

Use OPTIMIZE to note slow or inefficient code that may cause performance problems.

Use HACK to note "code smells" where questionable coding practices were used and should be refactored away.

Use REVIEW to note anything that should be looked at to confirm it is working as intended. For example: REVIEW: Are we sure this is how the client does X currently?

Use other custom annotation keywords if it feels appropriate, but be sure to document them in your project’s README or similar.

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:

;; 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 docstring 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.

;; 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."
  []
  ...)

Important tools such as cljdoc support Markdown in docstrings so leverage it for nicely formatted documentation.

;; good
(defn qzuf-number
  "Computes the [Qzuf number](https://wikipedia.org/qzuf) of the `coll`.
  Supported options in `opts`:

  | key           | description |
  | --------------|-------------|
  | `:finite-uni?`| Assume finite universe; default: `false`
  | `:complex?`   | If OK to return a [complex number](https://en.wikipedia.org/wiki/Complex_number); default: `false`
  | `:timeout`    | Throw an exception if the computation doesn't finish within `:timeout` milliseconds; default: `nil`

  Example:
  ```clojure
  (when (neg? (qzuf-number [1 2 3] {:finite-uni? true}))
    (throw (RuntimeException. \"Error in the Universe!\")))
  ```"
  [coll opts]
  ...)

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.

;; 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. Wrap them with [[..]] if you want to link to them.

;; 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.
  See also [[kangaroo]]."
  [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.
  See also kangaroo."
  [x]
  ...)

Docstrings should be composed of well-formed English sentences. Every sentence should start with a capitalized word, be grammatically coherent, and end with appropriate punctuation. Sentences should be separated with a single space.

;; good
(def foo
  "All sentences should end with a period (or maybe an exclamation mark).
  The sentence should be followed by a space, unless it concludes the docstring.")

;; bad
(def foo
  "all sentences should end with a period (or maybe an exclamation mark).
  The sentence should be followed by a space, unless it concludes the docstring.")

Indent multi-line docstrings by two spaces.

;; 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 docstring 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 docstring makes
it easier for tooling to display it correctly.")

Neither start nor end your docstrings with any whitespace.

;; good
(def foo
  "I'm so awesome."
  42)

;; bad
(def silly
  "    It's just silly to start a docstring 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.

;; good
(defn foo
  "docstring"
  [x]
  (bar x))

;; bad
(defn foo [x]
  "docstring"
  (bar x))
Note

Place docstrings for defprotocol methods after the argument vector:

(defprotocol MyProtocol
  "MyProtocol docstring"
  (foo [this x y z]
    "foo docstring")
  (bar [this]
    "bar docstring"))

Store your tests in a separate directory, typically test/yourproject/ (as opposed to src/yourproject/). Your build tool is responsible for making them available in the contexts where they are necessary; most templates will do this for you automatically.

Name your ns yourproject.something-test, a file which usually lives in test/yourproject/something_test.clj (or .cljc, cljs).

When using clojure.test, define your tests with deftest and name them something-test.

;; good
(deftest something-test ...)

;; bad
(deftest something-tests ...)
(deftest test-something ...)
(deftest something ...)

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 - its coordinates are com.stuartsierra/component.

Another approach that’s popular in the wild is to use a project (or organization) name as the groupId instead of domain name. Examples of such naming would be:

  • cider/cider-nrepl

  • nrepl/nrepl

  • nrepl/drawbridge

  • clj-commons/fs

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.

Deliver core functionality and integration points in separate artifacts. That way, consumers can consume your library without being constrained by your unrelated tooling preferences. For example, Component provides core functionality, and reloaded provides leiningen integration.

Code in a functional way, using mutation only when it makes sense.

Be consistent. In an ideal world, be consistent with these guidelines.

Use common sense.

One problem with style guides is that it’s often hard to remember all the guidelines and to apply them consistently. We’re only humans, after all. Fortunately, there are a bunch of tools that can do most of the work for us.

Tip
It’s a great idea run such tools as part of your continuous integration (CI). This ensure that all the code in one project is consistent with the style you’re aiming for.

There are some lint tools created by the Clojure community that might aid you in your endeavor to write idiomatic Clojure code.

  • kibit is a static code analyzer for Clojure which uses core.logic to search for patterns of code for which there might exist a more idiomatic function or macro.

  • clj-kondo is a linter that detects a wide number of discouraged patterns and suggests improvements, based on this style guide.

While most Clojure editors and IDEs can format the code, according to the layout guidelines outlined here, it’s always handy to have some command-line code formatting tools. There are a couple of options for Clojure that do a great job when it comes to formatting the code as suggested in this guide:

Note
When it comes to editors - Emacs’s clojure-mode by default will format the code exactly as outlined in the guide. Other editors might require some configuration tweaking to produce the same results.

This guide was started in 2013 by Bozhidar Batsov, following the success of a similar project he had created in the Ruby community.

Bozhidar was very passionate about both Clojure and good programming style and he wanted to bridge the gap between what was covered by the Clojure library coding guidelines and what the style guides for languages like Java, Python and Ruby would typically cover. Bozhidar still serves as the guide’s primary editor, but there’s an entire editor team supporting the project.

Since the inception of the guide we’ve received a lot of feedback from members of the exceptional Clojure community around the world. Thanks for all the suggestions and the support! Together we can make a resource beneficial to each and every Clojure developer out there.

The Clojure style guide is stewarded by an editor team of experienced Clojurists that aims to reduce all the input we get (e.g. feedback and suggestions) to a better reference for everyone.

The guide is still a work in progress - some guidelines are lacking examples, some guidelines don’t have examples that illustrate them clearly enough. Improving such guidelines is a great (and simple way) to help the Clojure community!

In due time these issues will (hopefully) be addressed - just keep them in mind for now.

Nothing written in this guide is set in stone. It’s my desire to work together with everyone interested in Clojure coding style, so that we could ultimately create a resource that will be beneficial to the entire Clojure community.

Feel free to open tickets or send pull requests with improvements. Thanks in advance for your help!

You can also support the style guide (and all my Clojure projects like CIDER, nREPL, orchard, etc) with financial contributions via one of the following platforms:

It’s easy, just follow the contribution guidelines below:

This guide is written in AsciiDoc and is published as HTML using AsciiDoctor. The HTML version of the guide is hosted on GitHub Pages.

Originally the guide was written in Markdown, but was converted to AsciiDoc in 2019.

A community-driven style guide is of little use to a community that doesn’t know about its existence. Tweet about the guide, share it with your friends and colleagues. Every comment, suggestion or opinion we get makes the guide just a little bit better. And we want to have the best possible guide, don’t we?


1. Queste linee guida sono pensate per essere applicate a Clojure stesso e a tutte le Clojure Contrib libraries.
2. A volte potremmo suggerire al lettore di considerare delle alternative.
3. Noterai che questa guida è molto simile in struttura a quella di Ruby, questo perchè essa è stata la nostra fonte primaria di ispirazione. Noterai anche che la guida allo stile per Ruby è molto più lunga, principalmente per la complessita del linguaggio.
4. Questa sezione è fortemente ispirata al PEP-8 di Python
5. *Utenti BSD/Solaris/Linux/macOS sono coperti di default,gli utenti Windows devono essere più cauti.
6. Queste linee guide sono basate su un blog post di Stuart Sierra.
7. Tecnicamente questo oscurerà la macro ns, ma è estremamente improbabile che tu ne abbia mai bisogno nel corpo di una funzione.
8. You can read more on the subject here.
9. This was first introduced in CIDER 0.10