Évaluation
L'évaluation peut avoir lieu dans de nombreux contextes :
- De manière interactive, dans l'interpréteur interactif (REPL)
- Sur une séquence de formulaires lus depuis un flux, via les fonctions `load`, `load-file`, `load-reader` ou `load-string`
- De manière programmatique, via la fonction `eval`
Les programmes Clojure sont composés d'expressions. Toute forme non traitée spécifiquement par une forme spéciale ou une macro est considérée par le compilateur comme une expression, étant évaluée pour produire une valeur. Il n'y a ni déclarations ni instructions, bien que les effets de bord des expressions puissent parfois être évalués et leurs valeurs ignorées. Dans tous les cas, l'évaluation est identique : un seul objet est considéré par le compilateur, évalué, et son résultat est renvoyé. Si une expression doit être compilée, elle le sera. Il n'y a pas d'étape de compilation séparée, ni besoin de s'inquiéter de l'interprétation d'une fonction définie. Clojure ne possède pas d'interpréteur.
Les chaînes de caractères, les nombres, les caractères, les valeurs `true`, `false`, `nil` et les mots-clefs sont évalués à eux-mêmes.
Résolution d'un symbole :
- S'il est qualifié par un espace de noms, sa valeur correspond à la valeur de la liaison de la variable globale qu'il désigne. Une erreur est levée si aucune variable globale ne porte ce nom, ou si la référence pointe vers une variable non publique située dans un espace de noms différent.
- S'il est qualifié par un paquet, sa valeur correspond à la classe Java qu'il désigne. Une erreur est levée si aucune classe ne porte ce nom.
- Si le qualificateur désigne une classe et que ce nom est :
- un champ statique de la classe, alors la valeur est la valeur de ce champ statique.
- une méthode statique de la classe, alors la valeur est une fonction Clojure qui appelle cette méthode statique. (Depuis la version 1.12)
- une méthode d'instance de la classe précédée d'un point, alors la valeur est une fonction Clojure qui appelle cette méthode d'instance. (Depuis la version 1.12)
- «new», alors la valeur est une fonction Clojure appelant le constructeur. (Depuis la version 1.12)
- Si le qualificateur désigne une classe ou un type primitif, et que le nom du symbole est :
- 1-9, alors la valeur est la classe du tableau dont le type de composant est le qualificateur et la dimension correspond à un chiffre. (depuis la version 1.12)
- Sinon, le symbole n'est pas qualifié et la première des conditions suivantes s'applique :
- S'il désigne une forme spéciale, celle-ci est considérée comme telle et doit être utilisée en conséquence.
- Dans une portée locale (par exemple, dans la définition d'une fonction ou avec l'instruction `let`), une recherche est effectuée pour déterminer s'il désigne une liaison locale (par exemple, un argument de fonction ou un nom lié par `let`). Si c'est le cas, la valeur est celle de la liaison locale.
- Une recherche est effectuée dans l'espace de noms courant pour vérifier s'il existe une correspondance entre le symbole et une classe. Si c'est le cas, le symbole est considéré comme désignant un objet de classe Java. Notez que les noms de classes désignent généralement des objets de classe, mais sont traités différemment dans certaines formes spéciales, par exemple `.` et `new`.
- Une recherche est effectuée dans l'espace de noms courant pour vérifier s'il existe une correspondance entre le symbole et une variable. Si c'est le cas, la valeur est celle de la liaison de la variable référencée par le symbole.
- Il s'agit d'une erreur.
Si un symbole possède des métadonnées, celles-ci peuvent être utilisées par le compilateur, mais ne feront pas partie de la valeur résultante.
Les vecteurs, les ensembles et les dictionnaires produisent des vecteurs et des dictionnaires (de hachage) dont le contenu correspond aux valeurs évaluées des objets qu'ils contiennent. Les éléments d'un vecteur sont évalués de gauche à droite, tandis que ceux d'un ensemble ou d'un dictionnaire sont évalués dans un ordre indéfini. Il en va de même pour les dictionnaires de métadonnées. Si le vecteur ou le dictionnaire contient des métadonnées, celles-ci deviendront les métadonnées de la valeur résultante :
- user=> (def x 1)
- user=> (def y 2)
- user=> ^{:x x} [x y 3]
- ^{:x 1} [1 2 3]
Une liste vide `()` est évaluée comme une liste vide.
Les listes non vides sont considérées comme des appels à des formes spéciales, des macros ou des fonctions. Un appel prend la forme `(opérateur opérandes*)`.
Les formes spéciales sont des primitives intégrées à Clojure effectuant des opérations de base. Si l'opérateur d'un appel est un symbole corresponant au nom d'une forme spéciale, l'appel s'effectue vers cette forme spéciale. Chaque forme est décrite individuellement dans la section «Formes spéciales».
Les macros sont des fonctions qui manipulent des formes, permettant ainsi une abstraction syntaxique. Si l'opérateur d'un appel est un symbole désignant une variable globale étant une fonction macro, cette fonction macro est appelée et reçoit les formes d'opérandes non évaluées. La valeur de retour de la macro est ensuite évaluée.
Si l'opérateur n'est ni une forme spéciale ni une macro, l'appel est considéré comme un appel de fonction. L'opérateur et les opérandes (le cas échéant) sont évalués de gauche à droite. Le résultat de l'évaluation de l'opérateur est converti en IFn (l'interface représentant les fonctions Clojure), puis la méthode `invoke()` est appelée sur ce résultat, en lui passant les arguments évalués. La valeur de retour de `invoke()` correspond à la valeur de l'expression d'appel. Si la forme d'appel de fonction contient des métadonnées, celles-ci peuvent être utilisées par le compilateur, mais ne feront pas partie de la valeur résultante. Notez que les formes spéciales et les macros peuvent avoir une évaluation de leurs arguments différente de la normale, comme décrit dans leurs entrées sous «Formes spéciales».
Tout objet autre que ceux mentionnés ci-dessus sera évalué à lui-même.
|
(load classpath-resource ...) (load-file filename) (load-reader reader) (load-string string) |
Ce qui précède décrit l'évaluation d'un formulaire unique. Les différents formulaires de chargement liront et évalueront séquentiellement l'ensemble des formulaires contenus dans la source. Ces ensembles de formulaires ont généralement des effets de bord, souvent sur l'environnement global, définissant des fonctions, etc.
Les fonctions de chargement s'exécutent dans un contexte temporaire, dans lequel `ns` dispose d'une liaison nouvelle. Cela signifie que si un formulaire a un effet sur cette variable (par exemple, dans un espace de noms), cet effet sera annulé à la fin du chargement. Les fonctions `load` et autres renvoient la valeur produite par la dernière expression.
| (eval form) |
Évalue la structure des données du formulaire (et non du texte !) et renvoie le résultat.
|
(eval (list + 1 2 3)) -> 6 |