REPL = lire eval imprimer boucle. Avancez dans le processus read-eval.
READ : Clojure voit la chaîne de caractères "(`a)"
l'analyse et aboutit à une structure de données. Au moment de la lecture, les macros du lecteur sont développées et il ne se passe pas grand-chose d'autre. Dans ce cas, le lecteur développe la rétro-citation et obtient ceci :
user> (read-string "(`a)")
((quote user/a))
EVAL : Clojure essaie d'évaluer cet objet. Les règles d'évaluation varient en fonction du type d'objet que vous regardez.
- Certains objets sont évalués comme eux-mêmes (nombres, chaînes de caractères, mots-clés, etc.).
- Un symbole est évalué en le résolvant dans un espace de nom pour obtenir une valeur (généralement).
- Une liste est évaluée par macro-expansion de la liste jusqu'à ce qu'il n'y ait plus de macros, puis par évaluation récursive du premier élément de la liste pour obtenir une valeur de valeur résultante puis en utilisant le valeur du premier élément de la liste pour décider de la marche à suivre. Si la première valeur est une forme spéciale, des choses spéciales se produisent. Sinon, la première valeur est traitée comme une fonction et appelée avec les valeurs du reste de la liste (obtenues en évaluant récursivement tous les éléments de la liste) comme paramètres.
- etc.
Se référer à clojure.lang.Compiler/analyzeSeq
dans le code source Clojure pour voir les règles d'évaluation des listes, ou bien clojure.lang.Compiler/analyzeSymbol
pour les symboles. Il y a beaucoup d'autres règles d'évaluation.
Exemple
Supposons que vous fassiez ça :
user> (user/a)
Le REPL finit par faire cela en interne :
user> (eval '(user/a))
Clojure voit que vous évaluez une liste, il évalue donc tous les éléments de la liste. Le premier (et seul) élément :
user> (eval 'user/a)
#<user$a__1811 user$a__1811@82c23d>
a
n'est pas une forme spéciale et cette liste n'a pas besoin d'être macro-expansée, donc le symbole a
est recherché dans l'espace de nom user
et le valeur résultante voici un fn
. Donc, ceci fn
s'appelle.
Votre code
Mais à la place, vous avez ceci :
user> (eval '((quote user/a)))
Clojure évalue le premier élément de la liste, qui est elle-même une liste.
user> (eval '(quote user/a))
user/a
Il a évalué le premier élément de cette sous-liste, quote
qui est une forme spéciale, donc des règles spéciales s'appliquent et elle retourne son argument (le symbole a
) non évalués.
Le symbole a
est le valeur dans ce cas comme le fn
était la valeur ci-dessus. Clojure traite donc le symbole lui-même comme une fonction et l'appelle. Dans Clojure, tout ce qui implémente la fonction Ifn
est appelable comme une interface fn
. Il se trouve que clojure.lang.Symbol
met en œuvre Ifn
. Un symbole appelé en tant que fonction n'attend qu'un seul paramètre, une collection, et il se cherche lui-même dans cette collection. Il est destiné à être utilisé comme ceci :
user> ('a {'a :foo})
:foo
C'est ce qu'il essaie de faire ici. Mais vous ne passez aucun paramètre, donc vous obtenez l'erreur "Wrong number of args passed to : Symbol" (il s'attend à une collection).
Pour que votre code fonctionne, il vous faudrait deux niveaux de eval
. Cela fonctionne, j'espère que vous pouvez voir pourquoi :
user> (eval '((eval (quote user/a))))
Hello, world
user> ((eval (first l)))
Hello, world
Notez que dans le code réel, l'utilisation de eval
directement est généralement une très mauvaise idée. Les macros sont de loin une meilleure idée. Je ne l'utilise ici que pour la démonstration.
Regardez Compiler.java
dans les sources de Clojure pour voir comment tout cela se passe. Ce n'est pas trop difficile à suivre.