Je comprends que les mots-clés en Clojure sont :keyword
. Mais à quoi sert ::
? Pourquoi cela semble-t-il avoir une liaison?
user=> :foo
:foo
user=> ::foo
:user/foo
Je comprends que les mots-clés en Clojure sont :keyword
. Mais à quoi sert ::
? Pourquoi cela semble-t-il avoir une liaison?
user=> :foo
:foo
user=> ::foo
:user/foo
Le double deux-points est là pour qualifier pleinement les mots-clés avec votre espace de noms actuel. Cela vise à éviter les conflits de noms pour les mots-clés qui ont une signification différente dans différentes bibliothèques. Sans des mots-clés pleinement qualifiés, vous pourriez accidentellement écraser certaines valeurs dans une carte et rompre la compatibilité avec une bibliothèque.
Quant à la justification fournie, je ne suis pas tout à fait sûr(e) de pourquoi, en premier lieu, quelqu'un voudrait bricoler une carte que la bibliothèque est censée gérer pour eux. Si c'est vraiment la principale motivation, une élucidation pourrait aider.
Un exemple pourrait être ring middleware (similaire aux filtres Servlet), avec lequel vous pouvez enrichir une carte décrivant une requête HTTP. Les clés namespace vous permettent d'écrire votre middleware personnalisé pour ajouter des valeurs supplémentaires sans vous soucier d'interférer avec tout autre middleware configuré dans votre application ring.
Tel que documenté pour Clojure ainsi que pour ClojureScript, les mots-clés ::
peuvent également être utilisés pour résoudre les alias de namespace. Par exemple, ::foo/bar
évaluera à :clojure.core/bar
si foo
est un alias de clojure.core
. Une exception du lecteur est levée si foo
ne se résout pas à un namespace.
Cette fonctionnalité est documentée là-bas - clojure.org/reference/reader#_literals - cljs.github.io/api/syntax/keyword-qualify
De plus, depuis Clojure 1.9, la syntaxe #:
et #::
est pertinente, utilisée avec une carte pour résoudre les mots-clés à l'intérieur de la carte, comme expliqué ici.
Le ::
est pour un "Fully-Qualified NameSpace", ou FQNS comme j'aime l'appeler, similaire au concept d'un FQDN. Il (::
) "s'étend" soit à un "alias requis" (par exemple, ::str
est un alias à la partie pointée de (:require [clojure.string :as str])
), ou le NS actuel dans lequel il se trouve (par exemple, ::
est un alias à (ns myproj.myns ...)
en haut du fichier actuel). Il a toujours un /
, contrairement à une clé simple qui n'a pas de /
.
Un FQNS (ayant ou résolvant à contenir un /
) peut prendre quelques formes, et doit être comparé à la syntaxe de clé de base non qualifiée qui utilise un seul :
et pas de /
. La syntaxe de clé "raccourci" des FQNS utilise des deux-points en tête (::
).
::k
est "local" et ::aa/k
est "aliassé")Ces clés sont "étendues" à un NS complet comme décrit précédemment. Pensez aux ::
comme représentant un nom long complet compressé. Ils peuvent contenir des points mais je trouve les tirets plus clairs. Notez que ce dernier contient un /
.
:bb.cc.dd/k
)Ces clés sont complètement explicitées, avec des points et des tirets de manière significative pour représenter les espaces de noms contenant les clés. Les points sont un détail de bas niveau (système de fichiers), mais représentent la hiérarchie parent/enfant, où aa.bb.cc
est aa
en tant que grand-parent, bb
en tant que parent, et cc
en tant qu'enfant. Un FQNS "explicite" a un seul :
mais contient un /
(par exemple, :aa/bb
, :cc.dd/ee
).
:k
)C'est la clé simple non qualifiée que vous connaissez depuis le premier jour.
Je trouve qu'il est utile que votre éditeur rende évident la forme que vous observez. Ici, vous pouvez voir dans la capture d'écran que le ::
est rouge pour le NS actuel, le NS complet explicite est bleu, le NS "aliassé" est vert, et la clé finale est violette.
Remarquez dans cet exemple (que vous pouvez tester dans un REPL) comment ces clés se "résolvent" (voir commentaires en fin de ligne). Il y a ici plus d'une douzaine de cas qui sont tous un peu différents.
(ns proj.ns1)
(ns proj.area.ns2)
(ns proj.ns3
(:require
[clojure.string :as str]
[clojure.data.avl :as d-a] ; ou da ou avl ou juste a ; soyez cohérent dans toute la base de code
[clojure.data.zip :as d.z] ; utiliser des points dans un alias est confus selon moi
[proj.area.ns2 :as ns2] ; certaines fonctions utilisées
[proj.ns1 :as-alias ns3])) ; commodité de clé, nouveau avec v1.11, évite les dépendances circulaires
(def m "Une map artificielle démontrant diverses formes de clés"
{:aa "bon" ;=> :aa ; typique : clé de base non qualifiée
::bb "bon" ;=> :proj.ns3/bb ; typique : notez que / est ajouté implicitement
::str "mauvais" ;=> :proj.ns3/str ; clé manquante
:ns3/cc "étrange" ;=> :ns3/cc ; espace de nom complet manquant malgré l'alias
:proj.area.ns4/dd "ok" ;=> :proj.area.ns4.dd ; l'espace de nom pourrait ne pas exister mais c'est ok
::ns2/ff "bon" ;=> :proj.area.ns2/ff ; pratique courante
:proj.area.ns2/gg "ok" ;=> :proj.area.ns2/gg ; mais c'est pourquoi nous avons `:as-alias`
:proj.ns1/hh "mauvais" ;=> :proj.ns1/hh ; plus clair d'utiliser simplement `::` pour le NS courant
:str/ii "mauvais" ;=> :str/ii ; un accident : str non étendu
::str/jj "bon" ;=> :clojure.string/jj ; et typique
::kk.ll "passable" ;=> :proj.ns3/kk.ll ; déroutant d'avoir des points dans une clé réelle
::d-a/mm.nn "passable" ;=> :clojure.data.json/mm.nn ; encore une fois, des points dans une clé
::d-a/oo "bon" ;=> :clojure.data.json/oo ; typique
::d.z/pp "passable" ;=> :proj.ns3/pp ; des points dans le qualifieur diffèrent de la structure de l'espace de nom
:random/qq "bon" ;=> :random/qq ; qualifié, mais pas un vrai NS
:other.random/rr "bon" ;=> :other.random/rr ; qualifié, mais pas un vrai NS
})
Notez que ce sont toutes des nouvelles clés que vous "créez". La clé :jj
n'existe pas réellement dans le NS clojure.string
, mais cela ne vous empêche pas de l'utiliser.
Une des choses les plus surprenantes est qu'un /
est ajouté dans l'expansion dans les utilisations locales (::
). Ainsi, ces trois sont celles sur lesquelles se concentrer pour se souvenir:
ÉCRIT RÉSOLU
:aa => :aa (basique)
::bb => :proj.ns1/bb (qualifié)
::ns3/cc => :proj.ns3/cc (qualifié, slash explicite)
Sur le côté gauche :
aa
et bb
semblent presque identiques, mais se résolvent très différemmentbb
et cc
semblent très différents, mais ils se résolvent en fait de manière très similaireJe souhaiterais un peu qu'il y ait ::/foo
au lieu de ::foo
car le /
implicite dans ce dernier rend le système irrégulier.
Il est important de connaître la plupart de ces cas de FQNS lorsque vous utilisez des bibliothèques comme malli ou spec ou integrant ou divers autres, car ils font un usage libéral de FQNS. Et pour tout projet de taille importante, il devient généralement nécessaire de qualifier certaines clés pour éviter les collisions.
Remarquez l'utilisation de :keys
et la sélection avec :
, ::
et aucun dans la déstructuration avec ceux-ci.
(let [{:keys [aa]} m] aa) ; "bon" (typique)
(let [{:keys [:aa]} m] aa) ; "bon" (fonctionne également avec :)
(let [{:keys [::aa]} m] aa) ; nil
(let [{:keys [::bb]} m] bb) ; "bon"
(let [{:keys [ns2/ff]} m] ff) ; nil
(let [{:keys [:ns2/ff]} m] ff) ; nil
(let [{:keys [::ns2/ff]} m] ff) ; "bon"
(let [{:keys [ns3/cc]} m] cc) ; "étrange"
(let [{:keys [:ns3/cc]} m] cc) ; "étrange"
(let [{:keys [other.random/rr]} m] rr) ; "bon"
(let [{:keys [:other.random/rr]} m] rr) ; "bon"
C'est une zone délicate de la syntaxe Clojure à garder claire, pourtant c'est une partie essentielle du code quotidien. Cela aide à réduire la confusion si vous suivez des guides de style NS tels que ceux-ci :
Notez qu'il peut y avoir des désaccords ici autour des conventions d'aliasing (« str » vs « string », points dans les alias, etc.), mais l'essentiel est de définir les conventions de votre base de code selon votre règles et de rester cohérent.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.