Eredeti forrás: https://github.com/bbatsov/clojure-style-guide
- Bevezetés
- Irányadó elvek
- Megjegyzés a következetességről
- Fordítások
- Forráskód elrendezése és szervezése
- Maximális sorhossz
- Tabok vs szóközök
- Törzs behúzása
- Függvényargumentumok igazítása
- Függvényargumentumok behúzása
- Kötések igazítása
- Map kulcsok igazítása
- Sorvégek
- Fájlok lezárása újsorral
- Zárójelek körüli szóközök
- Vesszők mellőzése a sorozatgyűjtemény-literálokban
- Vesszők használata map literálokban (opcionális)
- Záró zárójelek egy sorba gyűjtése
- Üres sorok a legfelső szintű formák között
- Nincs üres sor a definíciókon belül
- Nincs záró szóköz
- Névtérenként egy fájl
- Névtér deklaráció
- Ne használjunk egyszegmenses névtereket
- Névtérszegmensek maximális száma
- Átfogó ns forma
- Sortörések az ns-ben
- A :require előnyben a :use-szal szemben
- Az :require és :import sorok rendezése
- Használjunk idiomatikus névtér-aliasokat
- Recept a jó névtér-aliasokhoz
- Következetes névtér-aliasok használata
- Elnevezés
- Névtér elnevezési sémák
- Összetett szavú névtérszegmensek
- Függvények és változók
- Protokollok, rekordok, structok és típusok
- Predikátum függvények
- Nem biztonságos függvények
- Konverziós függvények
- Dinamikus változók
- Konstansok
- Nem használt azonosítók
- Idiomatikus nevek
- Függvények
- Újsor a függvénynév után (opcionális)
- A multimethod dispatch értékének elhelyezése
- Egysoros függvények
- Több aritású függvények behúzása
- A több aritású verziók sorrendje
- Függvény hossza
- A függvény paraméterek számának korlátja
- Pre- és postfeltételek
- Idiomák
- Névtér dinamikus módosítása
- Előrehivatkozások
- declare
- Magasabb rendű függvények
- Változók függvényen belül
- A clojure.core nevek árnyékolása
- Var kötés módosítása
- Nil punning
- Szekvenciák vektorrá alakítása
- Logikai értékké alakítás
- when vs if
- if-let
- when-let
- if-not
- when-not
- when-not vs if-not
- not=
- printf
- Rugalmas összehasonlító függvények
- Egy paraméteres függvényliterálok
- Többparaméteres függvényliterálok
- Ne használjunk felesleges névtelen függvényeket
- Ne használjunk több kifejezést függvényliterálokban
- Névtelen függvények vs complement, comp és partial
- Threading makrók
- Threading makrók és opcionális zárójelek
- Threading makrók igazítása
- A cond alapértelmezett ága
- condp vs cond
- case vs cond/condp
- Rövid formák a cond-ban
- Halmaz használata predikátumként
- inc és dec
- pos? és neg?
- list* vs cons
- Cukrozott Java-interop
- Tömör metaadat-notáció az igaz értékű jelzőknek
- Privát
- Privát változó elérése
- Metaadat körültekintő hozzárendelése
- Adatszerkezetek
- Listák kerülése
- Kulcsszavak használata hash kulcsként
- Literál gyűjtemény szintaxis használata
- Index alapú gyűjteményelérés kerülése
- Kulcsszavak használata függvényként map értékek lekéréséhez
- Gyűjtemények használata függvényként
- Kulcsszavak használata függvényként
- Átmeneti gyűjtemények kerülése
- Java gyűjtemények kerülése
- Java tömbök kerülése
- Típusok és rekordok
- Rekord konstruktorok
- Egyedi rekord konstruktorok
- Egyedi rekord konstruktorok elnevezése
- Mutáció
- Refek
- Agentek
- Atomok
- Math
- A clojure.math függvények preferálása az interoppal szemben
- Szövegek
- A clojure.string függvények preferálása interop helyett
- Kivételek
- Létező kivételtípusok újrahasználata
- A with-open preferálása a finally-vel szemben
- Makrók
- Ne írjunk makrót, ha függvény is megteszi
- Először a makró használatát írjuk meg, utána magát a makrót
- Bontsuk szét a bonyolult makrókat
- Makrók mint szintaktikus cukor
- Szintaktikusan idézett formák
- Gyakori metaadatok
- :added
- :changed
- :deprecated
- :superseded-by
- :see-also
- :no-doc
- Behúzási metaadat
- Megjegyzések
- Magától értetődő kód
- Fejléc kommentek
- Felső szintű kommentek
- Kódrészlet (sor) kommentek
- Margó (sorvégi) kommentek
- Pontosvessző utáni szóköz
- Angol szintaxis
- Kerüljük a fölösleges kommenteket
- Kommentek karbantartása
- #_ olvasómakró
- Refaktorálj, ne kommentelj
- Komment jelölések
- Jelölés felül
- Jelölő kulcsszavak
- Jelölések behúzása
- Jelölések aláírása és keltezése
- Ritka margó (sorvégi) jelölések
- TODO
- FIXME
- OPTIMIZE
- HACK
- REVIEW
- Egyedi jelölések dokumentálása
- Dokumentáció
- Dokumentációs stringek preferálása
- Dokumentációs string összefoglaló
- Markdown használata a dokumentációs stringekben
- Pozícionális argumentumok dokumentálása
- Hivatkozások dokumentálása
- Dokumentációs stringek nyelvtana
- Dokumentációs stringek behúzása
- Dokumentációs string eleji/végi szóközök
- Dokumentációs string elhelyezése a függvénynév után
- Tesztelés
- Tesztkönyvtár struktúra
- Teszt névtér elnevezése
- Tesztek elnevezése
- Könyvtár felépítése
- Könyvtár koordináták
- Függőségek minimalizálása
- Eszköz-függetlenség
- Létezés
- Legyél funkcionális
- Légy következetes
- Józan ész
- Eszközök
- Lint eszközök
- Kódformázók
- Történet
- Inspiráció forrásai
- Szerkesztőcsapat
- Hozzájárulás
- Hogyan lehet hozzájárulni?
- Kolofon
- Licenc
- Terjeszd a hírt
A példaképek fontosak.
— Alex J. Murphy rendőrtiszt / RoboCop
Ez a Clojure stílus útmutató bevált módszereket ajánl, hogy a valódi környezetben dolgozó Clojure programozók olyan kódot írjanak, amit más, szintén valódi környezetben dolgozó Clojure programozók karban tudnak tartani. Egy stílus útmutató akkor hasznos, ha a valós használatot tükrözi – különben hiába kiváló, ha az általa segíteni kívánt emberek már elutasították az abban foglalt elveket, nem fogják használni.
Az útmutató több, témakörök szerint rokon szabályokat tartalmazó szekcióra van bontva. Igyekeztünk feltüntetni a szabályok mögötti indoklást is (ha ez hiányzik, azt feltételeztük, hogy magától értetődő).
Nem a semmiből találtuk ki ezeket a szabályokat; nagyrészt a szerkesztők tapasztalatain, a Clojure közösség számos tagjának visszajelzésein és javaslatain, valamint több nagy tekintélyű Clojure-forrásanyagon alapulnak – például a 【0†"Clojure Programming"†www.clojurebook.com】 és a 【1†"The Joy of Clojure"†www.manning.com】 könyveken.
Semmi sincs kőbe vésve ebből. Ez az útmutató idővel változik, ahogy újabb konvenciók merülnek fel, és régiek válnak elavulttá magának a Clojure nyelvnek a változásai miatt.
Megjegyzés
A Clojure fejlesztői fenntartanak egy listát a 【2†könyvtárakra vonatkozó kódolási irányelvekről†clojure.org】【3†1】 is. Ezek egyike volt az ihletforrásoknak annak a dokumentumnak, amit épp olvasol.
A programokat az embereknek olvashatóra kell írni, és csak mellékesen a gépeknek futtatásra.
— Harold Abelson
Structure and Interpretation of Computer Programs
Közismert, hogy a kódot sokkal gyakrabban olvassák, mint írják. Az itt megfogalmazott irányelvek célja a kód olvashatóságának javítása és az, hogy egységessé tegye a Clojure-kódok széles skáláját. Emellett az is cél, hogy a Clojure valós használatát tükrözzék, ne valamilyen önkényes ideált. Amikor választanunk kellett egy nagyon elterjedt gyakorlat és egy szubjektíve jobb alternatíva között, az elterjedt gyakorlat ajánlása mellett döntöttünk【4†2】.
Van néhány terület, ahol a Clojure közösségben nincs egyértelmű konszenzus egy adott stílust illetően (például a szemantikus behúzás vs fix behúzás, szemantikus kommentek vs egységes kommentformátum stb.). Ilyen esetekben minden népszerű stílust elismerünk, és rajtad múlik, melyiket választod és alkalmazod következetesen.
Szerencsére a Clojure Lisp, és a Lispek alapvetően egyszerűek. Bár ez az útmutató pár évvel a Clojure megjelenése után készült (az első verzió 2013 elején jelent meg), látható volt, hogy a legtöbb „vadonban” található Clojure-kód meglehetősen egységes. Ezt részben az általunk már említett egyszerűségnek tulajdonítjuk, részben pedig annak, hogy a Clojuristák már a kezdetektől fogva átvették számos bevett Lisp dialektus (pl. Common Lisp és Scheme) stílusbeli konvencióit. Ez nagymértékben megkönnyítette az útmutató elkészítését – különösen a Ruby stíluskalauz közösségi létrehozásához képest, ami igazi frusztráló kínszenvedés volt【5†Community Ruby Style Guide†rubystyle.guide】【6†3】.
A Clojure híresen az egyszerűségre és érthetőségre van optimalizálva. Szeretnénk hinni, hogy ez az útmutató segít majd neked is a lehető legegyszerűbb és legtisztább kódot írni.
Az ostoba következetesség a kis elmék manója, melyet a kicsinyes államférfiak, filozófusok és papok imádnak.
— Ralph Waldo Emerson
Egy stílus útmutató lényege a következetesség【7†4】. Fontos, hogy következetes legyél ehhez az útmutatóhoz. Még ennél is fontosabb a projekteden belüli következetesség. A legfontosabb pedig az, hogy egy adott fájlon vagy függvényen belül legyél következetes.
Ugyanakkor tudd, mikor ne legyél következetes — néha az útmutató ajánlásai egyszerűen nem alkalmazhatók. Ha bizonytalan vagy, használd a legjobb ítélőképességedet. Nézz meg más példákat, és döntsd el, mi néz ki a legjobban. És ne habozz kérdezni!
Különösen: ne törj visszafelé kompatibilitást csak azért, hogy megfelelj ennek az útmutatónak!
Néhány jó ok, amiért figyelmen kívül hagyhatsz egy-egy irányelvet:
-
Ha az irányelv alkalmazása még az azt megszokott olvasók számára is rontaná a kód olvashatóságát.
-
Ha azért térsz el az irányelvtől, hogy a környező kóddal konzisztens maradj (esetleg történelmi okokból) — bár ez egyúttal alkalmat adhat arra, hogy XP-stílusban rendbe tedd más rendetlenségét.
-
Ha a szóban forgó kód megelőzi az irányelv bevezetését, és egyébként nem indokolt módosítani azt.
-
Ha a kódnak korábbi Clojure verziókkal is kompatibilisnek kell maradnia, amelyek nem támogatják az útmutató által javasolt funkcionalitást.
Az útmutató elérhető az alábbi nyelveken is:
-
【8†Kínai†github.com】
-
【9†Olasz†github.com】 (Folyamatban)
-
【10†Japán†github.com】
-
【11†Koreai†github.com】
-
【12†Portugál†github.com】 (Folyamatban)
-
【13†Orosz†github.com】
-
【14†Spanyol†github.com】
-
【15†Török†github.com】
Megjegyzés
Ezeket a fordításokat nem a hivatalos szerkesztőcsapat gondozza, így minőségük és teljességük változó lehet. A fordított verziók gyakran le vannak maradva az eredeti angol verzióhoz képest.
Majdnem mindenki meg van győződve arról, hogy minden stílus csúnya és olvashatatlan, kivéve a sajátját. Ha elhagyjuk a „kivéve a sajátját” részt, valószínűleg igazuk van…
— Jerry Coffin (a behúzásról)
Lehetőleg kerüljük, hogy a sorok hossza meghaladja a 80 karaktert.
Miért ragaszkodjunk a 80 karakteres sorhosszhoz a modern széles képernyős kijelzők korában?
Manapság sokan úgy érzik, hogy a 80 karakteres sorhossz korlátja csak a múlt maradványa, és ma már nincs sok értelme. Elvégre a modern kijelzők könnyedén meg tudnak jeleníteni 200+ karaktert egy sorban. Mégis, van néhány fontos előnye annak, ha ragaszkodunk a rövidebb kódsorokhoz.
Először is – számos tanulmány kimutatta, hogy az emberek sokkal gyorsabban olvasnak függőlegesen, és a túl hosszú szövegsorok akadályozzák az olvasást. Ahogy korábban említettük, ennek az útmutatónak az egyik vezérelve, hogy a kódot emberi fogyasztásra optimalizáljuk.
Továbbá, ha korlátozzuk a szükséges szerkesztőablak szélességet, lehetővé válik, hogy több fájlt nyissunk meg egymás mellett, és jól működik az olyan kódellenőrző eszközökkel is, amelyek két verziót egymás melletti oszlopokban mutatnak.
A legtöbb eszköz alapértelmezett sortörése megtöri a kód vizuális struktúráját, és nehezebben érthetővé teszi azt. A korlátot úgy érdemes megválasztani, hogy 80 karakteres ablakméretnél ne legyen szükség sortörésre, még akkor sem, ha az eszköz a legutolsó oszlopba valamilyen jelölést tesz töréskor. Néhány webes eszköz egyáltalán nem kínál dinamikus sortörést.
Néhány csapat határozottan előnyben részesíti a hosszabb sorokat. Ha egy kódot kizárólag vagy elsősorban egy olyan csapat tart karban, amely meg tud egyezni ebben a kérdésben, megengedhető a sorhossz-limit növelése akár 100 karakterre, vagy legfeljebb 120 karakterig. Kérjük, próbálj meg ellenállni a kísértésnek, hogy 120 karakternél is tovább menj.
Behúzáshoz szóközöket használjunk. Ne használjunk tabulátor karaktereket.
Használjunk 2 szóközt a törzsblokkok behúzására azoknál a formáknál, amelyek törzset tartalmaznak. Ide tartozik minden def
forma, minden speciális forma és makró, ami lokális kötéseket vezet be (pl. loop
, let
, when-let
), valamint sok más makró, mint a when
, cond
, as->
, cond->
, case
, with-*
stb.
;; jó
(when something
(something-else))
(with-out-str
(println "Hello, ")
(println "world!"))
;; rossz – négy szóköz
(when something
(something-else))
;; rossz – egy szóköz
(with-out-str
(println "Hello, ")
(println "world!"))
Igazítsuk függőlegesen a több sorba törő függvény- (vagy makró-) hívások argumentumait.
;; jó
(filter even?
(range 1 10))
;; rossz – az argumentum a függvénynév alá van igazítva (egy szóköz behúzás)
(filter even?
(range 1 10))
;; rossz – két szóköz behúzás
(filter even?
(range 1 10))
Az irányelv mögötti ok egyszerű – az argumentumokat könnyebb feldolgozni az emberi agynak, ha kiugranak és együtt vannak rendezve.
Megjegyzés
Általában tartsuk magunkat a fenti formázási szabályhoz, hacsak a rendelkezésre álló vízszintes hely korlátja ezt felül nem írja.
Használjunk egyetlen szóköznyi behúzást a függvény- (vagy makró-) hívások argumentumainak, ha egy sorban sincs argumentum a függvénynév mellett.
;; jó
(filter
even?
(range 1 10))
(or
ala
bala
portokala)
;; rossz – két szóköz behúzás
(filter
even?
(range 1 10))
(or
ala
bala
portokala)
Lehet, hogy furcsa különszabálynak tűnik ez azoknak, akik nem ismerik a Lisp hátteret, de a mögöttes logika egyszerű. A függvényhívások valójában listaliterek, és amikor több sorra tagolódnak, általában ugyanúgy vannak igazítva, mint bármely más gyűjteménytípus literáljai:
;; lista literál
(1
2
3)
;; vektor literál
[1
2
3]
;; halmaz literál
#{1
2
3}
Elismerjük, hogy a listaliterek nem túl gyakoriak Clojure-ben, ezért érthető, hogy sokak számára a lista csupán a hívás szintaxisát jelenti.
Mellékes haszonként a fenti behúzásnál az argumentumok is igazítva maradnak – történetesen egybeesnek a függvénynévvel is.
Szemantikus behúzás vs fix behúzás
Az az irányelv, hogy a törzzsel rendelkező makrók behúzása eltérjen minden más makró és függvény behúzásától, összefoglaló nevén „szemantikus behúzás”. Egyszerűen fogalmazva ez azt jelenti, hogy a kódot eltérően húzzuk be aszerint, hogy a behúzás utaljon a kód jelentésére.
Ennek a megközelítésnek az a hátránya, hogy okosabb formázó eszközöket kíván. Vagy fel kell dolgozniuk a macro
arglistáit és feltételezni, hogy az emberek konzisztensen nevezték el a paramétereiket, vagy pedig használniuk kell extra behúzási metaadatot.
A Clojure közösségben néhányan vitatják, hogy megéri-e ez, és inkább azt javasolják, hogy mindent egyszerűen ugyanúgy húzzunk be. Íme néhány példa:
;;; Fix behúzás
;;
;; makrók
(when something
(something-else))
(with-out-str
(println "Hello, ")
(println "world!"))
;; két soros függvényhívás
(filter even?
(range 1 10))
;; három soros függvényhívás
(filter
even?
(range 1 10))
Ez a javaslat kétségkívül teret nyert a közösségben, de szembemegy a Lisp hagyományok jelentős részével, és ellentmond az útmutató egyik fő céljának is – nevezetesen annak, hogy a kódot emberi fogyasztásra optimalizáljuk.
Van egy kivétel a fix behúzás szabály alól – az adatlisták (amelyek nem függvényhívások):
;;; Fix behúzás
;;
;; list literálok
;; még mindig így csináljuk
(1
2
3
4
5
6)
;; és így
(1 2 3
4 5 6)
;; ahelyett, hogy
(1 2 3
4 5 6)
;; vagy
(1
2
3
4
5
6)
Ez biztosítja, hogy a listák formázása konzisztens maradjon a többi gyűjteménytípus megszokott behúzásával.
Igazítsuk függőlegesen a let
(és let
-szerű) kötéseket.
;; jó
(let [thing1 "some stuff"
thing2 "other stuff"]
(foo thing1 thing2))
;; rossz
(let [thing1 "some stuff"
thing2 "other stuff"]
(foo thing1 thing2))
Igazítsuk függőlegesen a map kulcsait.
;; jó
{:thing1 thing1
:thing2 thing2}
;; rossz
{:thing1 thing1
:thing2 thing2}
;; rossz
{:thing1 thing1
:thing2 thing2}
Unix-stílusú sorvégjeleket használjunk.【16†5】
Tipp
Ha Gitet használsz, érdemes beállítani a következő konfigurációt, hogy megvédd a projektedet attól, hogy Windows sorvégék szivárogjanak bele:
$ git config --global core.autocrlf true
Minden fájl végét zárjuk egy újsor karakterrel.
Tipp
Ezt érdemes szerkesztőbeállítással automatikusan megoldani, nem manuálisan.
Ha egy nyitó zárójel ((
, {
vagy [
) előtt szöveg áll, vagy egy záró zárójel ()
, }
vagy ]
) után szöveg következik, szóközzel válasszuk el a szöveget a zárójeltől. Ugyanakkor ne hagyjunk szóközt egy nyitó zárójel után, ha utána szöveg jön, és ne tegyünk szóközt egy szöveg után közvetlenül egy záró zárójel elé.
;; jó
(foo (bar baz) quux)
;; rossz
(foo(bar baz)quux)
(foo ( bar baz ) quux)
A szintaktikus cukor pontosvesszőrákot okoz.
— Alan Perlis
Ne használjunk vesszőt a sorozatszerű gyűjtemény-literálok elemei között.
;; jó
[1 2 3]
(1 2 3)
;; rossz
[1, 2, 3]
(1, 2, 3)
Fontoljuk meg a vesszők és sortörések megfontolt használatát a map literálok olvashatóságának javítása érdekében.
;; jó
{:name "Bruce Wayne" :alter-ego "Batman"}
;; jó, és talán kissé olvashatóbb
{:name "Bruce Wayne"
:alter-ego "Batman"}
;; jó, és talán tömörebb
{:name "Bruce Wayne", :alter-ego "Batman"}
Az összes záró zárójelet egyetlen sorba tegyük, ahelyett, hogy külön sorokba kerülnének.
;; jó – egy sorban
(when something
(something-else))
;; rossz – külön sorokban
(when something
(something-else)
)
Egy üres sort hagyjunk a top-level formák között.
;; jó
(def x ...)
(defn foo ...)
;; rossz
(def x ...)
(defn foo ...)
;; rossz
(def x ...)
(defn foo ...)
Kivételt képez az az eset, amikor egymáshoz tartozó def
formákat csoportosítunk.
;; jó
(def min-rows 10)
(def max-rows 20)
(def min-cols 15)
(def max-cols 30)
Ne hagyjunk üres sorokat egy függvény vagy makró definíciójának közepén. Kivételt tehetünk páronkénti konstrukciók (mint a let
és cond
) csoportosítására, ha azok nem férnek ki egy sorba.
;; jó
(defn fibo-iter
([n] (fibo-iter 0 1 n))
([curr nxt n]
(cond
(zero? n) curr
:else (recur nxt (+' curr nxt) (dec n)))))
;; megengedhető – a sortörés a cond ágak csoportját jelzi
(defn fibo-iter
([n] (fibo-iter 0 1 n))
([curr nxt n]
(cond
(zero? n)
curr
:else
(recur nxt (+' curr nxt) (dec n)))))
;; rossz
(defn fibo-iter
([n] (fibo-iter 0 1 n))
([curr nxt n]
(cond
(zero? n) curr
:else (recur nxt (+' curr nxt) (dec n)))))
Időnként jó ötletnek tűnhet egy-egy üres sort beszúrni itt-ott egy hosszabb függvény definícióba, de ha idáig jutottál, érdemes elgondolkodni, hogy nem végez-e túl sok feladatot az a hosszú függvény, és nem lehetne-e részekre bontani.
Kerüljük a sorvégi (felesleges) whitespace-t.
Egy névtérhez egy fájlt és egy fájlhoz egy névteret használjunk.
;; jó
(ns foo.bar)
;; rossz
(ns foo.bar)
(ns baz.qux)
;; rossz
(in-ns quux.quuz)
(in-ns quuz.corge)
;; rossz
(ns foo.bar) vagy (in-ns foo.bar) több fájlban
Kerüljük az egyszavas (egyszegmenses) névterek használatát.
;; jó
(ns example.ns)
;; rossz
(ns example)
A névterek arra szolgálnak, hogy azonosítókat különítsenek el egymástól. Ha egyszegmenses névteret használsz, közvetlen konfliktusba kerülsz mindenki mással, aki szintén egyszegmenses névteret használ, így nagyobb eséllyel ütközöl más kódbázisokkal.
Gyakorlatilag ez azt jelenti, hogy könyvtárak soha ne használjanak egyszegmenses névteret, hogy elkerüljék a névütközéseket más könyvtárakkal. Persze a saját, privát alkalmazásodon belül azt csinálsz, amit akarsz.
Tipp
Általános gyakorlat az domain.library-name
vagy library-name.core
konvenció használata olyan könyvtáraknál, amelyeknek csak egy névterük van. A névterek elnevezéséről bővebben később lesz szó.
Más【17†egyéb okok†github.com】 is vannak, amiért érdemes elkerülni az egyszegmenses névtereket, ezért alaposan gondold át, mielőtt ilyet használsz.
Kerüljük a túl hosszú névtereket (azaz a 5-nél több szegmensből álló neveket).
Minden névteret egy átfogó ns
formával kezdjünk, ami refer
, require
és import
formákat tartalmaz – konvencionálisan ebben a sorrendben.
(ns examples.ns
(:refer-clojure :exclude [next replace remove])
(:require [clojure.string :as s :refer [blank?]])
(:import java.util.Date))
Ha több függőség van, célszerű mindegyiket külön sorba tenni. Ez megkönnyíti a rendezést, az olvasást, és tisztább diffeket eredményez a függőségek változásainál.
;; jobb
(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]))
;; jó
(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]))
;; rossz
(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]))
Az ns
formában részesítsük előnyben a :require :as
használatát a :require :refer
-rel szemben, és azt a :require :refer :all
-lal szemben. Előnyben részesítjük a :require
használatát a :use
-szal szemben; utóbbi formát tekintsük elavultnak új kódban.
;; jó
(ns examples.ns
(:require [clojure.zip :as zip]))
;; jó
(ns examples.ns
(:require [clojure.zip :refer [lefts rights]]))
;; megengedhető, ha indokolt
(ns examples.ns
(:require [clojure.zip :refer :all]))
;; rossz
(ns examples.ns
(:use clojure.zip))
Az ns
formában rendezzük alfabetikusan a require-öket és importokat. Ez javítja az olvashatóságot és elkerüli a duplikációt, különösen ha nagyon hosszú a require/import lista.
;; jó
(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]))
;; rossz
(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]))
Számos alap Clojure névtérnek vannak bevett aliasai, amelyeket érdemes használni a projektjeidben – pl. a clojure.string
névteret legtöbbször így importálják: [clojure.string :as str]
.
Megjegyzés
Elsőre úgy tűnhet, ez elfedi a clojure.core/str
függvényt, de valójában nem. Elfogadott, hogy a clojure.core/str
és a clojure.string/*
ugyanabban a névtérben str
és str/akármi
néven használható konfliktus nélkül.
;; jó
(ns ... (:require [clojure.string :as str] ...))
(str/join ...)
;; kevésbé jó – legyünk idiomatikusak és használjuk `str/` alias-t
(ns ... (:require [clojure.string :as string] ...))
(string/join ...)
Ahogy a következő részben megjegyezzük, általában bevett szokás az aliasnak a névter utolsó szegmensét használni, ha az egyedi, különben az utolsó két szegmenst, jellemzően elhagyva a fölösleges részeket, mint a clj
vagy core
.
A Clojure alap és Contrib névterek közül az alábbiak rendelkeznek ilyen mintát követő idiomatikus aliasokkal:
Név tér | Idiomatikus alias |
---|---|
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 |
Vannak továbbá alap és Contrib névterek, amelyeknek rövidebb idiomatikus aliasaik vannak:
Névtér | Idiomatikus alias |
---|---|
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 |
Gyakran használt közösségi könyvtárak között is soknak vannak széles körben elfogadott aliasai:
Névtér | Idiomatikus alias |
---|---|
cheshire.core | json |
clj-yaml.core | yaml |
clj-http.client | http |
hugsql.core | sql |
java-time | time |
next.jdbc | jdbc |
Fentebb áttekintettünk néhány népszerű névteret és azok idiomatikus aliasait. Észrevehetted, hogy nem teljesen egységesek:
-
clojure.string
aliasastr
-
clojure.pprint
aliasapp
-
clojure.walk
aliasawalk
-
clojure.spec.alpha
aliasas
Nyilvánvaló, hogy ami közös bennük, az az, hogy tömörek, de még hordoznak némi jelentést (a clojure.walk
w
aliasa ugyan tömör lenne, de nem túl beszédes).
De mi legyen az összes többi névtérrel, aminek nincs bevett aliasa? Nos, légy következetes az aliasok képzésében, különben a közös Clojure kódbázison dolgozók könnyen össze fognak zavarodni. Íme néhány szabály, amit kövess【18†6】:
-
Az alias legyen ugyanaz, mint a névtér neve, a kiinduló részek levágásával.
(ns com.example.application (:require [clojure.java.io :as io] [clojure.reflect :as reflect]))
-
Hagyd meg a végéből annyit, hogy mindegyik alias egyedi legyen.
[clojure.data.xml :as data.xml] [clojure.xml :as xml]
Tipp
Igen, az aliasokban lehet pont. Éljünk ezzel, ha szükséges.
-
Távolítsd el az aliasokból a fölösleges szavakat, mint „core” és „clj”.
[clj-time.core :as time] [clj-time.format :as time.format]
Egy projekten belül jó, ha következetesek a névtér-aliasok; pl. ne következetlenül használd ugyanarra a névtérre hol str
, hol string
aliasokat. Ha követed az előző két irányelvet, akkor ez magától teljesül, de ha saját egyedi aliasolási sémát alkalmazol, akkor is fontos, hogy a projekteden belül konzisztensen tedd.
A programozásban az egyetlen valódi nehézség a gyorsítótárak érvénytelenítése és a dolgok elnevezése.
— Phil Karlton
Névtér elnevezésekor részesítsük előnyben az alábbi két sémát:
-
projekt.modul
-
szervezet.projekt.modul
Ha a projekt.modul
sémát követjük és a projektünknek csak egyetlen (implementációs) névtere van, akkor azt általában projekt.core
-nak nevezzük. Kerüljük a projekt.core
nevet minden más esetben, mivel az informatívabb nevek mindig jobb választások.
Használjunk „lisp-case” írásmódot az összetett szavakból álló névtér-szegmensekhez (pl. bruce.project-euler
).
Megjegyzés
Sok nem-Lisp programozó közösség a lisp-case
-t kebab-case
-nek nevezi, de mindannyian tudjuk, hogy a Lisp sokkal előbb létezett, mint a kebab.
Használjunk lisp-case
írásmódot a függvény- és változóneveknél.
;; jó
(def some-var ...)
(defn some-fun ...)
;; rossz
(def someVar ...)
(defn somefun ...)
(def some_fun ...)
Használjunk CapitalCase
írásmódot a protokollok, rekordok, structok és típusok neveiben. (Az olyan mozaikszavakat, mint HTTP, RFC, XML hagyjuk nagybetűvel.)
Megjegyzés
A CapitalCase
ismert UpperCamelCase
, CapitalWords
vagy PascalCase
néven is.
A predikátum függvények (booleant visszaadó függvények) neve kérdőjellel végződjön (pl. even?
).
;; jó
(defn palindrome? ...)
;; rossz
(defn palindrome-p ...) ; Common Lisp stílus
(defn is-palindrome ...) ; Java stílus
Azoknak a függvényeknek/makróknak a nevét, amelyek STM tranzakciókban nem biztonságosan használhatók, felkiáltójellel zárjuk (pl. reset!
).
Használjunk ->
-t a to
helyett a konverziós függvények nevében.
;; jó
(defn f->c ...)
;; nem annyira jó
(defn f-to-c ...)
„Fülcskékkel” jelöljük azokat a változókat, amelyeket át akarunk kötni (dinamikusak).
;; jó
(def ^:dynamic *a* 10)
;; rossz
(def ^:dynamic a 10)
Ne használjunk speciális jelölést a konstansokhoz; mindent konstansnak tekintünk, hacsak nincs kifejezetten másképp jelezve.
;; jó
(def max-size 10)
;; rossz
(def MAX-SIZE 10) ; Java stílus
(def +max-size+ 10) ; Common Lisp stílus, globális konstans
(def *max-size* 10) ; Common Lisp stílus, globális változó
Megjegyzés
Híres ellenpélda a *clojure-version*
, ami szembemegy ezzel a konvencióval – ezt tekintsük történelmi furcsaságnak, és ne kövessük a példáját.
Használjunk _
karaktert azon destrukturálási célnevek és formális paraméternevek helyén, amelyek értékét a kód figyelmen kívül hagyja.
;; jó
(let [[a b _ c] [1 2 3 4]]
(println a b c))
(dotimes [_ 3]
(println "Hello!"))
;; rossz
(let [[a b c d] [1 2 3 4]]
(println a b d))
(dotimes [i 3]
(println "Hello!"))
Ugyanakkor, ha a kód megértését segíti, néha hasznos lehet kifejezetten elnevezni a nem használt paramétereket vagy a destrukturált mapeket. Ilyenkor tegyünk aláhúzást a név elé, jelezve, hogy a változó értéke nem lesz felhasználva.
;; jó
(defn myfun1 [context _]
(assoc context :foo "bar"))
(defn myfun2 [context {:keys [id]}]
(assoc context :user-id id))
;; még jobb
(defn myfun1 [context _user]
(assoc context :foo "bar"))
(defn myfun2 [context {:keys [id] :as _user}]
(assoc context :user-id id))
Kövessük a clojure.core
példáját az olyan idiomatikus nevek használatában, mint pred
és coll
.
-
függvényekben:
-
f
,g
,h
– függvény bemenő értéke -
n
– egész szám (gyakran méret) -
index
,i
– egész index -
x
,y
– számok -
xs
– szekvencia -
m
– map (asszociatív tömb) -
k
,ks
– kulcs, kulcsok -
v
,vs
– érték, értékek (kulcs/érték párokban) -
s
– string bemenet -
re
– reguláris kifejezés -
sym
– szimbólum -
coll
– kollekció -
pred
– predikátum függvény -
& more
– variádikus bemenet -
xf
– transzducer (xform
) -
ns
– namespace【19†7】
-
-
makrókban:
-
expr
– kifejezés -
body
– makró törzse -
binding
– makró binding vektor
-
-
metódusokban (ha
defprotocol
,deftype
,defrecord
,reify
stb. határozza meg őket):this
– az első paraméter neve, ami az objektumra utal – vagy alternatívaként valami, ami az objektumot jellemzi, de legyen konzisztens.
Ha nincs docstring, elhagyhatjuk az új sort a függvénynév és az argumentumvektor között a defn
esetén.
;; jó
(defn foo
[x]
(bar x))
;; jó
(defn foo [x]
(bar x))
;; rossz
(defn foo
[x] (bar x))
A multimethod dispatch-val
értékét tegyük ugyanarra a sorra, mint a függvény nevét.
;; jó
(defmethod foo :bar [x] (baz x))
(defmethod foo :bar
[x]
(baz x))
;; rossz
(defmethod foo
:bar
[x]
(baz x))
(defmethod foo
:bar [x]
(baz x))
Lehetőség szerint hagyjuk el az új sort az argumentumvektor és egy rövid függvény törzse között.
;; jó
(defn foo [x]
(bar x))
;; jó kis törzsű függvénynél
(defn foo [x] (bar x))
;; jó több aritású függvényeknél
(defn foo
"Két aritásom van."
([x] (foo x 1))
([x y]
(if (predicate? x)
(bar x)
(baz x))))
;; rossz
(defn foo
[x] (if (predicate? x)
(bar x)
(baz x)))
Egy függvény több aritású definíciójában minden aritást függőlegesen, a paramétereivel egy vonalba indentáljunk.
;; jó
(defn foo
"Két aritásom van."
([x]
(foo x 1))
([x y]
(+ x y)))
;; rossz – extra behúzás
(defn foo
"Két aritásom van."
([x]
(foo x 1))
([x y]
(+ x y)))
A függvény aritásait rendezd a legkevesebb paraméterestől a legtöbb felé. Gyakori esetben egy K paraméteres aritás teljesen meghatározza a függvény működését, az N < K paraméteres aritások részben alkalmazzák a K paraméteres verziót, az N > K paraméteres aritások pedig a K paraméteres verziót hívják meg többször (pl. fold-olva).
;; jó – könnyű átlátni az n-edik aritást
(defn foo
"Két aritásom van."
([x]
(foo x 1))
([x y]
(+ x y)))
;; elfogadható – a további aritások a két paraméteres aritást használják
(defn foo
"Két aritásom van."
([x y]
(+ x y))
([x]
(foo x 1))
([x y z & more]
(reduce foo (foo x (foo y z)) more)))
;; rossz – látszólag ok nélkül összekevert sorrend
(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)))
Kerüljük a 10 sornál hosszabb függvényeket. Ideális esetben a legtöbb függvény rövidebb, mint 5 sor.
Kerüljük azokat a függvényeket, amelyeknek három-négy darabnál több pozíciós paramétere van.
Részesítsük előnyben a függvény pre- és postfeltételeit a függvénytörzsön belüli ellenőrzésekkel szemben.
;; jó
(defn foo [x]
{:pre [(pos? x)]}
(bar x))
;; rossz
(defn foo [x]
(if (pos? x)
(bar x)
(throw (IllegalArgumentException. "x must be a positive number!")))
Kerüljük a névtér-manipuláló függvények (például require
és refer
) használatát. REPL környezeten kívül ezek teljesen szükségtelenek.
Kerüljük az előre deklarált hivatkozásokat. Néha szükség van rájuk, de a gyakorlatban ritkán fordul elő.
Használjunk declare
-t, ha elengedhetetlen az előrehivatkozás.
Részesítsük előnyben a magasabb rendű függvényeket (pl. map
) a loop/recur
helyett.
Ne definiáljunk függvényen belül globális változókat.
;; nagyon rossz
(defn foo []
(def x 5)
...)
Ne árnyékoljunk clojure.core
neveket lokális kötésekkel.
;; rossz – a clojure.core/map teljes nevével kell hivatkozni a függvényen belül
(defn foo [map]
...)
Használjunk alter-var-root
-ot def
helyett egy var értékének megváltoztatására.
;; jó
(def thing 1) ; thing értéke most 1
; csinálunk valamit thing-gel
(alter-var-root #'thing (constantly nil)) ; thing értéke most nil
;; rossz
(def thing 1)
; csinálunk valamit thing-gel
(def thing nil)
; thing értéke most nil
Használjuk a seq
-et mint terminálási feltételt annak tesztelésére, hogy egy szekvencia üres-e (ezt a technikát néha nil punningnak hívják).
;; jó
(defn print-seq [s]
(when (seq s)
(prn (first s))
(recur (rest s))))
;; rossz
(defn print-seq [s]
(when-not (empty? s)
(prn (first s))
(recur (rest s))))
Ha egy szekvenciát vektorrá kell alakítani, inkább vec
-et használjunk into
helyett.
;; jó
(vec some-seq)
;; rossz
(into [] some-seq)
Ha valamit valódi boolean értékké (true
vagy false
) kell konvertálni, használjuk a boolean
függvényt.
;; jó
(boolean (foo bar))
;; rossz
(if (foo bar) true false)
Megjegyzés
Ne feledd, hogy Clojure-ban csak a false
és a nil
„hamisak”. Minden más értéke true
lesz, amikor a boolean
függvénynek adod.
Ritkán van szükség tényleges boolean értékre Clojure-ban, de hasznos tudni, hogyan érhetjük el, ha mégis kell.
Használjunk when
-t az if
helyett, ha csak a feltétel igaz ágát kezeljük, például (if feltétel (valami…))
vagy (if … (do …))
esetén.
;; jó
(when pred
(foo)
(bar))
;; rossz
(if pred
(do
(foo)
(bar)))
Használjunk if-let
-et let
+ if
helyett.
;; jó
(if-let [result (foo x)]
(something-with result)
(something-else))
;; rossz
(let [result (foo x)]
(if result
(something-with result)
(something-else)))
Használjunk when-let
-et let
+ when
helyett.
;; jó
(when-let [result (foo x)]
(do-something-with result)
(do-something-more-with result))
;; rossz
(let [result (foo x)]
(when result
(do-something-with result)
(do-something-more-with result)))
Használjunk if-not
-ot (if (not …) …)
helyett.
;; jó
(if-not pred
(foo))
;; rossz
(if (not pred)
(foo))
Használjunk when-not
-ot (when (not …) …)
helyett.
;; jó
(when-not pred
(foo)
(bar))
;; rossz
(when (not pred)
(foo)
(bar))
Használjunk when-not
-ot (if-not … (do …))
helyett.
;; jó
(when-not pred
(foo)
(bar))
;; rossz
(if-not pred
(do
(foo)
(bar)))
Használjunk not=
-et (not (= …))
helyett.
;; jó
(not= foo bar)
;; rossz
(not (= foo bar))
Részesítsük előnyben a printf
használatát a (print (format …))
helyett.
;; jó
(printf "Hello, %s!\n" name)
;; oké
(println (format "Hello, %s!" name))
Összehasonlításoknál használjuk ki, hogy a Clojure <
, >
stb. függvényei tetszőleges számú argumentumot elfogadnak.
;; jó
(< 5 x 10)
;; rossz
(and (> x 5) (< x 10))
Egyparaméteres függvényliterálokban a %
használata ajánlott a %1
helyett.
;; jó
#(Math/round %)
;; rossz
#(Math/round %1)
Többparaméteres függvényliterálokban a %
helyett a %1
használata ajánlott.
;; jó
#(Math/pow %1 %2)
;; rossz
#(Math/pow % %2)
Ne burkoljunk függvényeket névtelen függvényekbe, ha nincs rá szükség.
;; jó
(filter even? (range 1 10))
;; rossz
(filter #(even? %) (range 1 10))
Ne használjunk függvényliterálokat, ha a függvény törzse egynél több formából áll.
;; jó
(fn [x]
(println x)
(* x 2))
;; rossz (ilyenkor explicit do formára lenne szükség)
#(do (println %)
(* % 2))
Részesítsük előnyben a névtelen függvényeket a complement
, comp
és partial
helyett, mivel ez a legtöbbször egyszerűbb kódhoz vezet【20†8】.
;; jó
(filter #(not (some-pred? %)) coll)
;; elmegy
(filter (complement some-pred?) coll)
Tegyük fel, hogy (:require [clojure.string :as str])
...
;; jó
(map #(str/capitalize (str/trim %)) ["top " " test "])
;; elmegy
(map (comp str/capitalize str/trim) ["top " " test "])
A comp
nagyon hasznos transzducer láncok összefűzésére.
;; jó
(def xf
(comp
(filter odd?)
(map inc)
(take 5)))
;; jó
(map #(+ 5 %) (range 1 10))
;; elmegy
(map (partial + 5) (range 1 10))
Részesítsük előnyben a ->
(thread-first) és ->>
(thread-last) makrók használatát a mélyen egymásba ágyazott kifejezésekkel szemben.
;; jó
(-> [1 2 3]
reverse
(conj 4)
prn)
;; kevésbé jó
(prn (conj (reverse [1 2 3])
4))
;; jó
(->> (range 1 10)
(filter even?)
(map (partial * 2)))
;; kevésbé jó
(map (partial * 2)
(filter even? (range 1 10)))
Nem kötelező zárójeleket tenni a threading makrókban olyan függvények köré, amelyeknek nincs argumentuma, ezért csak akkor tegyük ki őket, ha szükséges.
;; jó
(-> x fizz :foo first frob)
;; rossz – a zárójelek feleslegesek, csak bezavarják a képet
(-> x (fizz) (:foo) (first) (frob))
;; jó – ha van argumentum, szükséges a zárójel
(-> x
(fizz a b)
:foo
first
(frob x y))
A threading makrók ->
(thread-first) és ->>
(thread-last) argumentumait igazítsuk egymás alá.
;; jó
(->> (range)
(filter even?)
(take 5))
;; rossz
(->> (range)
(filter even?)
(take 5))
Használjunk :else
kulcsszót a cond
catch-all (minden más eset) feltételeként.
;; jó
(cond
(neg? n) "negatív"
(pos? n) "pozitív"
:else "zero")
;; rossz
(cond
(neg? n) "negatív"
(pos? n) "pozitív"
true "zero")
Részesítsük előnyben a condp
használatát a cond
helyett, ha az összehasonlító predikátum és kifejezés nem változik.
;; jó
(cond
(= x 10) :ten
(= x 20) :twenty
(= x 30) :thirty
:else :dunno)
;; sokkal jobb
(condp = x
10 :ten
20 :twenty
30 :thirty
:dunno)
Ha a vizsgált kifejezések fordítási időben konstansok, részesítsük előnyben a case
használatát a cond
vagy condp
helyett.
;; jó
(cond
(= x 10) :ten
(= x 20) :twenty
(= x 30) :forty
:else :dunno)
;; jobb
(condp = x
10 :ten
20 :twenty
30 :forty
:dunno)
;; legjobb
(case x
10 :ten
20 :twenty
30 :forty
:dunno)
Használjunk rövid formát a cond
(és hasonló) kifejezésekben. Ha ez nem lehetséges, adjunk vizuális segítséget a páronkénti csoportosításhoz kommentekkel vagy üres sorokkal.
;; jó
(cond
(test1) (action1)
(test2) (action2)
:else (default-action))
;; elmegy
(cond
;; 1. teszteset
(test1)
(long-function-name-which-requires-a-new-line
(complicated-sub-form
(-> 'which-spans multiple-lines)))
;; 2. teszteset
(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)))
Használjunk halmazt predikátumként, amikor megfelelő.
;; jó
(remove #{1} [0 1 2 3 4 5])
;; rossz
(remove #(= % 1) [0 1 2 3 4 5])
;; jó
(count (filter #{\a \e \i \o \u} "mary had a little lamb"))
;; rossz
(count (filter #(or (= % \a)
(= % \e)
(= % \i)
(= % \o)
(= % \u))
"mary had a little lamb"))
Használjunk (inc x)
és (dec x)
formát a (+ x 1)
és (- x 1)
helyett.
Használjuk a (pos? x)
, (neg? x)
és (zero? x)
formákat a (> x 0)
, (< x 0)
és (= x 0)
helyett.
Használjunk list*
-t több egymásba ágyazott cons
helyett.
;; jó
(list* 1 2 3 [4 5])
;; rossz
(cons 1 (cons 2 (cons 3 [4 5])))
Használjuk a cukrozott Java interop formákat.
;;; objektum létrehozás
;; jó
(java.util.ArrayList. 100)
;; rossz
(new java.util.ArrayList 100)
;;; statikus metódus hívás
;; jó
(Math/pow 2 10)
;; rossz
(. Math pow 2 10)
;;; példány metódus hívás
;; jó
(.substring "hello" 1 3)
;; rossz
(. "hello" substring 1 3)
;;; statikus mező elérés
;; jó
Integer/MAX_VALUE
;; rossz
(. Integer MAX_VALUE)
;;; példány mező elérés
;; jó
(.someField some-object)
;; rossz
(. some-object someField)
Használjuk a tömör metaadat jelölést olyan metaadathoz, amely csak kulcsszó kulcsú és true
értékű mezőket tartalmaz.
;; jó
(def ^:private a 5)
;; rossz
(def ^{:private true} a 5)
Jelöljük a kód privát részeit.
;; jó
(defn- private-fun [] ...)
(def ^:private private-var ...)
;; rossz
(defn private-fun [] ...) ; egyáltalán nem privát
(defn ^:private private-fun [] ...) ; túl bőbeszédű
(def private-var ...) ; egyáltalán nem privát
Egy privát var eléréséhez (pl. teszteléskor) használjuk a @#'some.ns/var
formát.
Figyeljünk oda, hogy pontosan mihez rendelünk hozzá metaadatot.
;; a metaadatot az `a` varhoz rendeljük hozzá
(def ^:private a {})
(meta a) ;=> nil
(meta #'a) ;=> {:private true}
;; a metaadatot az üres hash-map értékhez rendeljük hozzá
(def a ^:private {})
(meta a) ;=> {:private true}
(meta #'a) ;=> nil
Jobb, ha 100 függvény működik egy adatszerkezeten, mint ha 10 függvény működik 10 különböző adatszerkezeten.
— Alan J. Perlis
Kerüljük listák használatát általános adatok tárolására (hacsak éppen listára van szükségünk).
Részesítsük előnyben a kulcsszavak használatát a hash map kulcsaiként.
;; jó
{:name "Bruce" :age 30}
;; rossz
{"name" "Bruce" "age" 30}
Amennyiben lehet, használjunk literál szintaxist a gyűjtemények létrehozásához. Azonban halmazok definiálásakor csak akkor használjunk literál szintaxist, ha az értékek fordítási időben konstansok.
;; jó
[1 2 3]
#{1 2 3}
(hash-set (func1) (func2)) ; értékek futásidőben meghatározva
;; rossz
(vector 1 2 3)
(hash-set 1 2 3)
#{(func1) (func2)} ; runtime exception, ha (func1) = (func2)
Kerüljük a gyűjtemények index alapján történő elérését, amikor csak lehet.
Ha lehetséges, használjunk kulcsszó-függvényeket a mapek értékeinek lekérdezésére.
(def m {:name "Bruce" :age 30})
;; jó
(:name m)
;; bővebb a kelleténél
(get m :name)
;; rossz – NullPointerException lehetőség
(m :name)
Éljünk azzal, hogy a legtöbb gyűjtemény használható függvényként az elemeire.
;; jó
(filter #{\a \e \o \i \u} "this is a test")
;; rossz – túl csúnya, hogy leírjuk
Éljünk azzal, hogy a kulcsszavak függvényként használhatók egy gyűjteményen.
((juxt :a :b) {:a "ala" :b "bala"})
Kerüljük az átmeneti (transient
) gyűjtemények használatát, kivéve teljesítménykritikus kódrészekben.
Kerüljük a Java gyűjtemények használatát.
Kerüljük a Java tömbök használatát, kivéve interop esetén és olyan teljesítménykritikus kódban, ahol sok primitív típussal dolgozunk.
Ne használjuk az interop szintaxist típus- és rekordpéldányok létrehozására. A deftype
és defrecord
automatikusan létrehozza a konstruktor függvényeket. Ezeket használjuk az interop szintaxis helyett, mert így egyértelmű, hogy deftype
-ról vagy defrecord
-ról van szó. Részletekért lásd 【21†ezt a cikket†stuartsierra.com】.
(defrecord Foo [a b])
(deftype Bar [a b])
;; jó
(->Foo 1 2)
(map->Foo {:b 4 :a 3})
(->Bar 1 2)
;; rossz
(Foo. 1 2)
(Bar. 1 2)
Megjegyzés: a deftype
nem hoz létre map->Type
konstruktort. Ilyen csak a rekordokhoz jár.
Adjunk hozzá egyedi típus-/rekord-konstruktorokat, ha szükséges (pl. hogy ellenőrizzük az értékeket rekord létrehozáskor). Részletekért lásd 【21†ezt a cikket†stuartsierra.com】.
(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))
Nyugodtan alkalmazzunk tetszőleges elnevezési konvenciót vagy struktúrát az ilyen egyedi konstruktorokra.
Ne definiáljuk felül az automatikusan generált típus-/rekord-konstruktor függvényeket. Az emberek elvárják, hogy ezek bizonyos módon működjenek, és ennek megváltoztatása sérti a legkisebb meglepetés elvét. Részletekért lásd 【21†ezt a cikket†stuartsierra.com】.
(defrecord Foo [num])
;; jó
(defn make-foo
[num]
{:pre [(pos? num)]}
(->Foo num))
;; rossz
(defn ->Foo
[num]
{:pre [(pos? num)]}
(Foo. num))
Fontoljuk meg, hogy minden I/O műveletet az io!
makróba csomagoljunk, hogy elkerüljük a kellemetlen meglepetéseket, ha véletlenül tranzakcióban hívnánk ilyen kódot.
Kerüljük a ref-set
használatát, amikor csak lehet.
(def r (ref 0))
;; jó
(dosync (alter r + 5))
;; rossz
(dosync (ref-set r 5))
Próbáljuk a tranzakciók méretét (a bennük végzett munka mennyiségét) minél kisebben tartani.
Kerüljük, hogy ugyanazon Ref-fel egyszerre rövid és hosszú tranzakciók is interakcióba lépjenek.
A send
-et csak CPU-igényes, I/O-t nem végző vagy más szálakon nem blokkoló műveletekhez használjuk.
A send-off
-ot olyan műveletekhez használjuk, amelyek blokkolhatnak, aludhatnak vagy egyéb módon lefoglalhatják a szálat.
Kerüljük atomok módosítását STM tranzakciókon belül.
Lehetőleg használjunk swap!
-ot a reset!
helyett.
(def a (atom 0))
;; jó
(swap! a + 5)
;; kevésbé jó
(reset! a 5)
Részesítsük előnyben a clojure.math
névtér függvényeit a (Java) interop vagy saját megoldások helyett.
;; jó
(clojure.math/pow 2 5)
;; elmegy
(Math/pow 2 5)
A JDK java.lang.Math
csomagja számos hasznos matematikai függvényhez biztosít hozzáférést. Az 1.11 előtti Clojure verziók ezekhez interop útján fértek hozzá, de ennek voltak korlátai felfedezhetőség, primitívek teljesítménye, magasabb rendű alkalmazás és hordozhatóság terén. Az új clojure.math
névtér wrapper függvényeket nyújt a java.lang.Math
metódusaihoz long
és double
változatokkal, gyors primitív meghívással.
Részesítsük előnyben a clojure.string
névtér szövegmanipulációs függvényeit a közvetlen Java interop vagy saját megoldások helyett.
;; jó
(clojure.string/upper-case "bruce")
;; rossz
(.toUpperCase "bruce")
Megjegyzés
Számos új függvény került a clojure.string
névtérbe a Clojure 1.8-ban (index-of
, last-index-of
, starts-with?
, ends-with?
és includes?
). Kerüld ezek használatát, ha régebbi Clojure-verziókat is támogatnod kell.
Használjunk meglévő kivételtípusokat. Az idiomatikus Clojure kód – amikor kivételt dob – a szabványos kivételtípusok egyikét dobja (pl. java.lang.IllegalArgumentException
, java.lang.UnsupportedOperationException
, java.lang.IllegalStateException
, java.io.IOException
).
Részesítsük előnyben a with-open
használatát a finally
helyett.
Ne írjunk makrót, ha egy függvény is megfelelő lenne.
Először készítsünk egy példát arra, hogyan szeretnénk használni a makrót, és csak utána írjuk meg magát a makrót.
A bonyolult makrókat lehetőség szerint bontsuk kisebb függvényekre.
Egy makró általában csak szintaktikus cukrot biztosítson, a lényegi munka pedig egy hétköznapi függvényben történjen. Ezzel javítjuk a kompozíthatóságot.
Részesítsük előnyben a szintaxis szerint idézett formákat a listák kézi összefűzése helyett.
Ebben a szakaszban áttekintjük a névterekhez és változókhoz kapcsolódó gyakori metaadatokat, amelyeket a Clojure fejlesztőeszközök ki tudnak használni.
A nyilvános API egy elemének bevezetési verzióját leggyakrabban az :added
metaadattal dokumentáljuk.
(def ^{:added "0.5"} foo
42)
(ns foo.bar
"Egy nagyon hasznos névtér."
{:added "0.8"})
(defn ^{:added "0.5"} foo
(bar))
Tipp
Ha SemVer-t követsz, érdemes elhagyni a patch verziót. Ez azt jelenti, hogy pl. 0.5
-öt használj 0.5.0
helyett. Ez minden verzióhoz kötődő metaadatra vonatkozik.
A nyilvános API egy elemének megváltoztatását leggyakrabban :changed
metaadattal dokumentáljuk. Ez a metaadat csak varokra értelmes, és ritkán kellene használni, hiszen a publikus API viselkedésének megváltoztatása általában rossz ötlet.
Ha mégis megteszed, a legjobb egyértelművé tenni a felhasználók számára.
;; verzióval jelölve
(def ^{:added "0.5"
:changed "0.6"} foo
43)
A publikus API-k elavulttá nyilvánításának leggyakoribb módja a :deprecated
metaadat használata. Általában értékként annak a verziónak a számát adjuk meg, amelyben az elem elavulttá vált verziózott szoftverek (pl. könyvtárak) esetén, vagy egyszerűen true
értéket verziót nem követő szoftvereknél (pl. egy webalkalmazás).
;;; jó
;;
;; verzióval jelölve
(def ^{:deprecated "0.5"} foo
"Használd helyette a `bar` függvényt."
42)
(ns foo.bar
"Elavult névtér."
{:deprecated "0.8"})
(defn ^{:deprecated "0.5"} foo
(bar))
;; verzió nélkül
(defn ^:deprecated foo
(bar))
;;; rossz
;;
;; a docstring használata az elavultság jelzésére
(def foo
"ELAVULT: Használd helyette a `bar`-t."
42)
(ns foo.bar
"ELAVULT: Egy elavult névtér.")
Gyakran a :deprecated
-ot kombináljuk a :superseded-by
metaadattal, hiszen általában van egy újabb API, ami felváltja a régit.
Általában varok esetében nem teljesen minősített nevet használunk, ha a kiváltó új elem ugyanabban a névtérben van, egyébként teljesen minősített nevet.
;; verzióval jelölve
(def ^{:deprecated "0.5"
:superseded-by "bar"} foo
"Használd helyette a `bar` függvényt."
42)
(ns foo.bar
"Elavult névtér."
{:deprecated "0.8"
:superseded-by "foo.baz"})
(defn ^{:deprecated "0.5"
:superseded-by "bar"} foo
(bar))
;; verzió nélkül
(defn ^{:deprecated true
:superseded-by "bar"} foo
(bar))
Tipp
Érdemes megfontolni a :supersedes
metaadat hozzáadását az újabb API-khoz – ez lényegében a :superseded-by
inverze.
Időnként érdemes rámutatni kapcsolódó varokra/névterekre, amelyek a könyvtárad felhasználói számára érdekesek lehetnek. Ennek leggyakoribb módja a :see-also
metaadat, amely egy vektorban sorolja fel a kapcsolódó elemeket. Ha varokról van szó – azonos névtéren belüli elemeknél nem szükséges a teljes név.
;; ugyanazon névtérbeli varokra hivatkozik
(def ^{:see-also ["bar" "baz"]} foo
"Egy nagyon hasznos var."
42)
;; más névtérbeli varokra hivatkozik
(defn ^{:see-also ["top.bar" "top.baz"]} foo
(bar))
Megjegyzés
Számos Clojure eszköz a docstringből is megpróbál kinyerni hivatkozásokat más varokra, de sokkal egyszerűbb és egyértelműbb a :see-also
metaadat használata erre a célra.
Az olyan dokumentációs eszközök, mint 【22†Codox†github.com】 vagy 【23†cljdoc†github.com】 felismerik a :no-doc
metaadatot. Ha egy var vagy névtér :no-doc
metaadatot kap, az jelzi ezeknek az eszközöknek, hogy hagyják ki az API dokumentációból.
Egy teljes névtér kihagyása az API dokumentációból:
(ns ^:no-doc my-library.impl
"Belső megvalósítási részletek")
...
Egy dokumentált névtéren belüli varok kihagyása:
(ns my-library.api)
;; a privát függvények nincsenek dokumentálva
(defn- clearly-private []
...)
;; ahogy azok a publikus függvények sem, amelyek :no-doc metaadatot kapnak
(defn ^:no-doc shared-helper []
...)
;; ez a függvény dokumentálva lesz
Ez utóbbi komment utolsó sora valószínűleg a dokumentált függvény illusztrációja, de most nincs tartalom utána, mert csak egy példát mutatott.
Megjegyzés: Az útmutató aszerint hagyja abba itt a kódblokkot és magyarázatot, a dokumentált függvény példáját valószínűleg a shared-helper
utáni sor kommentje jelzi, hogy "ez a függvény dokumentálva lesz". Ez a magyarázat véget ért a példával.
A jó kód a saját legjobb dokumentációja. Mielőtt kommentet írsz, kérdezd meg magadtól: "Hogyan javíthatnám a kódot, hogy erre a kommentre ne is legyen szükség?" Javítsd a kódot, majd dokumentáld, hogy még egyértelműbb legyen.
— Steve McConnell
Törekedj arra, hogy a kódod minél inkább magától értetődő legyen. Ha ez nem sikerül, kövesd e szekció többi irányelvét.
Írjunk szakaszoló fejléckommenteket legalább négy pontosvesszővel. Ezek jellemzően kódrészletek főbb szekcióit választják el vagy fontos gondolatokat írnak le. Gyakran egy szakaszkommentet több felső szintű komment követ.
;;;; Szekció komment/Fejléc
;;; Foo...
;;; Bar...
;;; Baz...
Három pontosvesszővel írjunk top-level (mindentől független) kommenteket.
;;; Én egy felső szintű komment vagyok.
;;; Egy definíción kívül helyezkedem el.
(defn foo [])
Megjegyzés
Bár a klasszikus Lisp hagyomány ;;;
-t használ a felső szintű kommentekhez, sok „vadonbeli” Clojure kódban ;;
-t vagy akár csak ;
-t láthatsz ugyanerre a célra.
Egy adott kódrészlethez tartozó kommentet az adott kód elé és azzal egy vonalba igazítva írjunk, két pontosvesszővel.
(defn foo [x]
;; Én egy sor/kódrészlet komment vagyok.
x)
Megjegyzés
Bár a klasszikus Lisp hagyomány ;;
-t diktál a sor-kommentekhez, sok „vadonbeli” Clojure kódban csak ;
-t használnak.
A sorvégi kommenteket egy pontosvesszővel írjuk.
(defn foo [x]
x ; Én egy sor/kódrészlet komment vagyok.
)
Kerüljük az ilyen kommenteket, ha lógó záró zárójel(ek) alakulnának ki miattuk.
Mindig tegyünk legalább egy szóközt a pontosvessző (komment) karakter után, mielőtt a szöveg következik.
;;;; Frob Grovel
;;; Ebben a kódrészben fontos mondanivalók vannak:
;;; 1. Foo.
;;; 2. Bar.
;;; 3. Baz.
(defn fnord [zarquon]
;; Ha zob, akkor veeblefitz.
(quux zot
mumble ; Zibblefrotz.
frotz))
Ha egy komment egynél több szóból áll, nagybetűvel kezdődjön és használjon írásjeleket. A mondatokat egy 【27†egy szóköz†en.wikipedia.org】 válassza el egymástól.
;; This is a good comment.
;; this is a bad comment
Nyilvánvalóan a helyesírás nem a kommentek legfontosabb aspektusa, de egy kis plusz erőfeszítés jobb élményt nyújt a kommentek olvasóinak.
Kerüljük a fölösleges kommenteket.
;; rossz
(inc counter) ; növeli a counter változót egyel
Tartsuk karban a kommenteket. Egy elavult komment rosszabb, mint ha nem is lenne komment.
Ha egy adott formát ki kell kommentezni, inkább a #_
olvasómakrót használjuk egy sima komment helyett.
;; jó
(+ foo #_(bar x) delta)
;; rossz
(+ foo
;; (bar x)
delta)
A jó kód olyan, mint egy jó vicc – nincs szüksége magyarázatra.
— Russ Olsen
Kerüld, hogy rossz kódot kommenttel magyarázz. Inkább refaktoráld a kódot, hogy önmagáért beszéljen. („Tedd, vagy ne tedd. De ne próbáld.” – Yoda)
A jelöléseket általában közvetlenül az érintett kódelem fölé írjuk.
;; jó
(defn some-fun
[]
;; FIXME: Replace baz with the newer bar.
(baz))
;; rossz
;; FIXME: Replace baz with the newer bar.
(defn some-fun
[]
(baz))
A jelölő kulcsszót kettőspont és szóköz követi, majd a problémát leíró megjegyzés.
;; jó
(defn some-fun
[]
;; FIXME: Replace baz with the newer bar.
(baz))
;; rossz – nincs kettőspont a jelölés után
(defn some-fun
[]
;; FIXME Replace baz with the newer bar.
(baz))
;; rossz – nincs szóköz a kettőspont után
(defn some-fun
[]
;; FIXME:Replace baz with the newer bar.
(baz))
Ha több sor kell a probléma leírásához, a következő sorokat annyival indokoljuk beljebb, amennyit az első sor is be volt húzva.
;; jó
(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))
;; rossz
(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))
Lásd el a jelölést a monogramoddal és dátummal, hogy könnyen ellenőrizhető legyen, mennyire aktuális.
(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))
Ha a probléma annyira nyilvánvaló, hogy bármi egyéb magyarázat redundáns lenne, a jelölést kivételesen a sor végén, külön magyarázat nélkül is hagyhatjuk. Ezt a használatot tekintsük kivételesnek, ne normának.
(defn bar
[]
(sleep 100)) ; OPTIMIZE
Használd a TODO
-t olyan hiányzó funkció vagy szolgáltatás megjelölésére, amit később pótolni kell.
Használd a FIXME
-t hibás kód megjelölésére, amit ki kell javítani.
Használd az OPTIMIZE
-t lassú vagy nem hatékony kód megjelölésére, amely teljesítményproblémát okozhat.
Használd a HACK
-et a "code smell" (gyanús kód) megjelölésére, ahol kérdéses megoldásokat alkalmaztunk, és később tisztábbra kell refaktorálni.
Használd a REVIEW
-t bármi olyan megjelölésére, amit át kell nézni, hogy biztosan a kívánt módon működik-e. Például: REVIEW: Biztos, hogy az ügyfél jelenleg így csinálja X-et?
Használhatsz más egyedi kommentjelölő kulcsszavakat is, ha szükségesnek érzed, de mindenképp dokumentáld őket a projekted README
-jében vagy hasonló helyen.
A docstringek az elsődleges eszközei a Clojure kód dokumentálásának. Sok definíciós forma (pl. def
, defn
, defmacro
, ns
) támogatja a docstringet, és általában jó ötlet is élni velük, függetlenül attól, hogy az adott var publikus vagy privát.
Ha egy definíciós forma nem támogatja közvetlenül a docstringet, metaadatként még mindig elláthatod :doc
kulccsal.
Ez a szekció áttekinti a Clojure kód dokumentálásának néhány gyakori konvencióját és bevált gyakorlatát.
Ha egy forma közvetlenül támogat docstringet, azt részesítsd előnyben a :doc
metaadattal szemben:
;; jó
(defn foo
"Ez a függvény nem csinál sokat."
[]
...)
(ns foo.bar.core
"Ez egy nagyszerű könyvtár.")
;; rossz
(defn foo
^{:doc "Ez a függvény nem csinál sokat."}
[]
...)
(ns ^{:doc "Ez egy nagyszerű könyvtár."}
foo.bar.core)
Legyen a docstring első sora egy teljes, nagybetűvel kezdődő mondat, amely tömören leírja a szóban forgó var funkcióját. Így az eszközök (Clojure szerkesztők, IDE-k) könnyen meg tudják jeleníteni a docstring rövid összefoglalóját különböző helyeken.
;; jó
(defn frobnitz
"Ez a függvény frobnitzet hajt végre.
Gnorwatzot fog csinálni ennek érdekében, de csak bizonyos
körülmények között."
[]
...)
;; rossz
(defn frobnitz
"Ez a függvény frobnitzet hajt végre. Gnorwatzot fog csinálni
ennek érdekében, de csak bizonyos körülmények között."
[]
...)
Az olyan fontos eszközök, mint 【28†cljdoc†github.com】 támogatják a Markdown szintaxist a docstringekben, ezért használjuk ki ezt a lehetőséget a szépen formázott dokumentáció érdekében.
;; jó
(defn qzuf-number
"Kiszámítja a [Qzuf számot](https://wikipedia.org/qzuf) a `coll` számára.
Az `opts` lehetséges kulcsai:
| kulcs | leírás |
| --------------- | ------ |
| `:finite-uni?` | Véges univerzumot feltételez; alapértelmezés: `false` |
| `:complex?` | Megengedjük komplex [szám](https://hu.wikipedia.org/wiki/Komplex_szám) visszatérését; alapértelmezés: `false` |
| `:timeout` | Kivételt dob, ha a számítás nem fejeződik be `:timeout` milliszekundum alatt; alapértelmezés: `nil` |
Példa:
```clojure
(when (neg? (qzuf-number [1 2 3] {:finite-uni? true}))
(throw (RuntimeException. \"Error in the Universe!\")))
```"
[coll opts]
...)
Dokumentáljunk minden pozícionális argumentumot, és tegyük őket backtick (`) közé, hogy a szerkesztők és IDE-k azonosítani tudják őket, és esetleg extra funkcionalitást kínálhassanak rájuk.
;; jó
(defn watsitz
"A Watsitz egy `frob`-ot znoottá alakít.
Ha a `frob` negatív, a znoot dühös lesz."
[frob]
...)
;; rossz
(defn watsitz
"A Watsitz egy frobot znoottá alakít.
Ha a frob negatív, a znoot dühös lesz."
[frob]
...)
Tegyük a docstringben előforduló var hivatkozásokat backtick közé, hogy az eszközök fel tudják ismerni őket. Ha linkelni is szeretnénk rájuk, tegyük dupla kapcsos zárójel közé [[..]]
.
;; jó
(defn wombat
"Úgy viselkedik, mint a `clojure.core/identity`, kivéve amikor nem.
Bemenetként `x` értéket vesz, és visszaadja azt. Ha kedve tartja.
Lásd még [[kangaroo]]."
[x]
...)
;; rossz
(defn wombat
"Úgy viselkedik, mint a clojure.core/identity, kivéve amikor nem.
Bemenetként x értéket vesz, és visszaadja azt. Ha kedve tartja.
Lásd még kangaroo."
[x]
...)
A docstringek jól formált magyar mondatokból álljanak. Minden mondat nagybetűvel kezdődjön, nyelvtanilag helyes legyen, és megfelelő írásjellel végződjön. A mondatokat egy szóköz válassza el.
;; jó
(def foo
"Minden mondatnak ponttal (vagy esetleg felkiáltójellel) kell végződnie.
A mondat után szóköz következzen, hacsak nem a docstring végét jelenti.")
;; rossz
(def foo
"minden mondatnak ponttal (vagy esetleg felkiáltójellel) kell végződnie.
A mondat után szóköz következzen, hacsak nem a docstring végét jelenti.")
A több soros docstringeket két szóközzel indentáljuk.
;; jó
(ns my.ns
"Valójában egy névteret is lehet dokumentálni.
Ez egy jó hely a névtér céljának és talán az általános konvencióknak a leírására. Figyeld meg, hogy a docstring behúzásának *elmulasztása* megkönnyíti az eszközöknek a helyes megjelenítést.")
;; rossz
(ns my.ns
"Valójában egy névteret is lehet dokumentálni.
Ez egy jó hely a névtér céljának és talán az általános konvencióknak a leírására. Figyeld meg, hogy a docstring behúzásának elmulasztása megkönnyíti az eszközöknek a helyes megjelenítést.")
Ne kezdjünk vagy zárjunk docstringet whitespace karakterrel.
;; jó
(def foo
"Nagyon király vagyok."
42)
;; rossz
(def silly
" Ostobaság szóközökkel kezdeni egy docstringet.
Ugyanolyan ostoba, mint szóközökkel befejezni. "
42)
Amikor docstringet adunk – különösen egy függvényhez a fenti formában – ügyeljünk, hogy a docstring a függvénynév után kerüljön, ne az argumentumvektor után. Az utóbbi nem szintaktikailag érvénytelen, nem okoz hibát, de a string így a függvény törzsének részeként kerül értelmezésre, ahelyett, hogy a var dokumentációja lenne.
;; jó
(defn foo
"docstring"
[x]
(bar x))
;; rossz
(defn foo [x]
"docstring"
(bar x))
Megjegyzés
A defprotocol
metódusainál a docstringet az argumentumvektor után helyezzük el:
(defprotocol MyProtocol
"MyProtocol docstring"
(foo [this x y z]
"foo docstring")
(bar [this]
"bar docstring"))
Tartsuk a teszteket külön könyvtárban, tipikusan test/yourproject/
alatt (szemben a src/yourproject/
könyvtárral). A build eszköz feladata, hogy szükség esetén ezeket elérhetővé tegye; a legtöbb projekt sablon ezt automatikusan beállítja.
Nevezd el a teszt névteret yourproject.something-test
formára; ez a fájl jellemzően a test/yourproject/something_test.clj
(vagy .cljc
, .cljs
) alatt található.
Ha a clojure.test
-et használod, a teszteket deftest
-tel definiáld, és nevezd el őket valami-test
alakra.
;; jó
(deftest something-test ...)
;; rossz
(deftest something-tests ...)
(deftest test-something ...)
(deftest something ...)
Ha mások által is használható könyvtárakat publikálsz, kövesd a 【29†Central Repository útmutatót†central.sonatype.org】 a groupId
és artifactId
megválasztásához. Ez segít elkerülni a névütközéseket és elősegíti a minél szélesebb körű felhasználhatóságot. Jó példa erre a 【30†Component†github.com】 – a koordinátája com.stuartsierra/component
.
Egy másik elterjedt megközelítés az, ha projekt- (vagy szervezet-) nevet használsz groupId
-ként a domain helyett. Ilyen példák:
-
cider/cider-nrepl
-
nrepl/nrepl
-
nrepl/drawbridge
-
clj-commons/fs
Kerüld a szükségtelen függőségeket. Például, ha egy háromsoros kis segédfüggvényt bemásolhatsz a projektedbe, az általában jobb, mint egy függőség bevonása, ami százával hoz be olyan varokat, amiket nem is használsz.
A fő funkcionalitást és az integrációs pontokat külön artefaktumokba szállítsd. Így a felhasználók eszközfüggetlenül is használhatják a könyvtáradat, anélkül hogy a te kiegészítő eszközválasztásaidhoz lennének kötve. Például a 【30†Component†github.com】 a fő funkcionalitást biztosítja, míg a 【31†reloaded†github.com】 a Leiningen integrációt.
Funkcionális stílusban programozz, a mutációt csak akkor használd, ha van értelme.
Légy következetes. Ideális esetben légy következetes ezekhez az irányelvekhez is.
Használd a józan eszed.
Egy stílus útmutatóval az a probléma, hogy gyakran nehéz minden irányelvre emlékezni és azokat következetesen alkalmazni. Végül is csak emberek vagyunk. Szerencsére van egy csomó eszköz, ami elvégzi helyettünk a munka nagy részét.
Tipp
Érdemes ezeket az eszközöket a folyamatos integráció (CI) részeként futtatni. Így biztosíthatod, hogy egy projekt összes kódja következetes legyen az általad kitűzött stílussal.
A Clojure közösség készített néhány lint eszközt, amelyek segíthetnek idiomatikus Clojure kódot írni:
-
【32†kibit†github.com】 – egy statikus kódelemző Clojure-hoz, ami a 【33†core.logic†github.com】 segítségével olyan kódrészeket keres, ahol létezhet idiomatikusabb függvény vagy makró az adott minta helyett.
-
【34†clj-kondo†github.com】 – egy linter, ami számos nem ajánlott mintát felismer és javításokat javasol rájuk, ezen stílus útmutató alapján.
Bár a legtöbb Clojure szerkesztő és IDE tudja formázni a kódot az itt leírt layout szabályok szerint, hasznosak lehetnek a parancssori kódformázó eszközök is. Clojure-hoz több ilyen is elérhető, amelyek kiválóan formázzák a kódot az útmutató javaslatai szerint:
-
【35†cljfmt†github.com】
-
【36†cljstyle†github.com】
-
【37†zprint†github.com】 (ennek konfigurálását a közösségi formázási szabályokhoz 【38†itt†github.com】 ismertetik)
Megjegyzés
Szerkesztők terén – Emacs clojure-mode
módja alapból pontosan az útmutatóban leírtak szerint formázza a kódot. Más szerkesztők esetleg igényelnek némi konfigurálást a hasonló eredményhez.
Ezt az útmutatót 2013-ban indította 【39†Bozhidar Batsov†github.com】, a 【40†hasonló projekt†rubystyle.guide】 sikerén felbuzdulva, amit a Ruby közösség számára hozott létre.
Bozhidar szenvedélyesen szerette a Clojure-t és a jó programozási stílust, és át akarta hidalni azt a rést, ami a 【2†Clojure könyvtárak kódolási irányelvei†clojure.org】 és aközött állt fenn, amit más nyelvek (pl. Java, Python, Ruby) stíluskalauzai tipikusan lefedtek. Bozhidar ma is az útmutató elsődleges szerkesztője, de egy egész szerkesztőcsapat támogatja a projektet.
Az útmutató indulása óta rengeteg visszajelzést kaptunk a kiváló nemzetközi Clojure közösség tagjaitól. Köszönjük az összes javaslatot és támogatást! Együtt egy minden Clojure fejlesztő számára hasznos forrást hozhatunk létre.
Számos ember, könyv, előadás, cikk és más stíluskalauz hatott a közösségi Clojure stílus útmutatóra. Íme néhány közülük:
-
【41†"The Elements of Style"†en.wikipedia.org】
-
【42†"The Elements of Programming Style"†en.wikipedia.org】
-
【43†Python Style Guide (PEP-8)†www.python.org】
-
【40†Community Ruby Style Guide†rubystyle.guide】
-
【44†Google Common Lisp Style Guide†google.github.io】
-
【45†scheme-style†community.schemewiki.org】
-
【2†Clojure könyvtár kódolási irányelvek†clojure.org】
-
【0†"Clojure Programming"†www.clojurebook.com】
-
【1†"The Joy of Clojure"†www.manning.com】
-
【46†"Elements of Clojure"†elementsofclojure.com】
-
【47†"Clojure Applied"†pragprog.com】
-
【48†Stuart Sierra: "Clojure Do's and Don'ts" blog sorozata†stuartsierra.com】
A Clojure stílus útmutatót egy tapasztalt Clojure-szerkesztőkből álló csapat gondozza, amelynek célja, hogy az összes beérkező inputot (pl. visszajelzések, javaslatok) egy jobb referenciává gyúrja mindenki számára.
-
【49†Bozhidar Batsov†metaredux.com】
-
【50†Alex Miller†insideclojure.org】
-
【51†Daniel Compton†danielcompton.net】
-
【52†Sean Corfield†corfield.org】
Az útmutató még mindig fejlesztés alatt áll – néhány irányelvhez hiányoznak példák, más irányelveknek nincs elég szemléletes példája. Az ilyen irányelvek javítása remek (és egyszerű) módja annak, hogy segítsd a Clojure közösséget!
Idővel remélhetőleg ezek a problémák megoldódnak – egyelőre tartsd őket észben.
Az útmutatóban semmi sincs kőbe vésve. Szeretném, ha mindenkit, akit érdekel a Clojure kódolási stílusa, együtt dolgozna velem azon, hogy végül egy olyan erőforrást hozzunk létre, amely az egész Clojure közösség javára válik.
Nyugodtan nyiss hibajegyeket vagy küldj pull requesteket a fejlesztésekért. Előre is köszönjük a segítségedet!
A stílus útmutatót (és minden Clojure projektemet, mint a CIDER, nREPL, orchard stb.) anyagilag is támogathatod az alábbi platformokon:
-
【53†GitHub Sponsors†github.com】
-
【54†ko-fi†ko-fi.com】
-
【55†Patreon†www.patreon.com】
-
【56†PayPal†www.paypal.me】
Egyszerű, csak kövesd az alábbi hozzájárulási irányelveket:
-
【57†Forkold†help.github.com】 a 【58†bbatsov/clojure-style-guide†github.com】 repót GitHubon.
-
Végezdd el a fejlesztést vagy hibajavítást egy külön feature branch-ben.
-
Adj egy 【59†jó leírást†tbaggery.com】 a változtatásaidhoz.
-
Push-old fel a feature branchet a GitHubra.
-
Küldj egy 【60†Pull Requestet†help.github.com】.
Ez az útmutató 【61†AsciiDoc†asciidoc.org】 formátumban készült, és HTML-ként kerül publikálásra a 【62†AsciiDoctor†asciidoctor.org】 segítségével. Az útmutató HTML verziója a GitHub Pages-en van hostolva.
Eredetileg az útmutató Markdownban íródott, de 2019-ben AsciiDocra konvertálták.
【75†Kép: Creative Commons License†guide.clojure.style】 Jelen mű licencelve van a 【63†Creative Commons Nevezd meg! 3.0 Unported†creativecommons.org】 licenc alatt.
Egy közösség által alakított stíluskalauz mit sem ér, ha a közösség nem is tud a létezéséről. Írj róla tweetet, oszd meg barátaiddal és kollégáiddal! Minden hozzászólás, javaslat vagy vélemény egy kicsit jobbá teszi az útmutatót. Márpedig a lehető legjobb útmutatót szeretnénk, nem igaz?
【64†1】. Ezeket az irányelveket magára a Clojure-ra és a hivatalos Clojure Contrib könyvtárakra szánják.
【65†2】. Esetenként javasolhatunk alternatívákat is az olvasónak.
【66†3】. Észre fogod venni, hogy a Clojure stílus útmutató felépítésében nagyon hasonlít a Ruby stíluskalauzhoz, ami a fő ihletforrás volt. A Ruby stíluskalauz sokkal hosszabb, főleg a Ruby nyelv összetettsége miatt.
【67†4】. Ez a szakasz nagyban a Python PEP-8-on alapul.
【68†5】. *BSD/Solaris/Linux/macOS felhasználók eleve így működnek, Windows felhasználóknak viszont erre figyelniük kell.
【69†6】. Ezek az irányelvek 【70†Stuart Sierra egy blogposztján†stuartsierra.com】 alapulnak.
【71†7】. Technikailag ez árnyékolja az ns
makrót, de nagyon valószínűtlen, hogy egy függvény törzsében szükség legyen rá.
【72†8】. Erről bővebben 【73†itt olvashatsz†ask.clojure.org】.
【74†9】. Ezt először a CIDER 0.10 vezette be.
Last updated 2024-03-06 06:43:06 UTC