Explication
Le problème est que doc
est une macro, pas une fonction. Vous pouvez le vérifier à l'aide de la fonction source
dans le repl.
(source doc)
; (defmacro doc
; "Prints documentation for a var or special form given its name"
; {:added "1.0"}
; [name]
; (if-let [special-name ('{& fn catch try finally try} name)]
; (#'print-doc (#'special-doc special-name))
; (cond
; (special-doc-map name) `(#'print-doc (#'special-doc '~name))
; (resolve name) `(#'print-doc (meta (var ~name)))
; (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
Si vous êtes nouveau dans Clojure (et dans les lisps), vous n'avez peut-être pas encore rencontré les macros. En guise d'explication rapide et dévastatrice, je dirais que là où les fonctions opèrent sur des objets de type évaluée les macros opèrent sur le code non évalué c'est-à-dire le code source lui-même.
Cela signifie que lorsque vous tapez
(doc (rand-nth (keys (ns-publics 'clojure.core))))
doc
tente d'opérer sur la ligne de code réelle - (rand-nth (keys (ns-publics 'clojure.core)))
- plutôt que le résultat évalué (le symbole retourné par this). Le code n'étant rien d'autre qu'une liste en Clojure, c'est pourquoi l'erreur vous dit qu'une liste ne peut pas être transformée en symbole.
Solution
Donc, ce que vous voulez vraiment faire, c'est évaluer le code, puis appeler doc
sur le résultat. Nous pouvons le faire en écrivant une autre macro qui évalue d'abord le code que vous lui donnez, puis le transmet à doc
.
(defmacro eval-doc
[form]
(let [resulting-symbol (eval form)]
`(doc ~resulting-symbol)))
Vous pouvez passer eval-doc
formes arbitraires et il les évaluera avant de les transmettre à doc
. Nous sommes maintenant prêts à partir.
(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))
Editer :
Bien que ce qui précède fonctionne assez bien dans la réalité, si vous utilisez une compilation anticipée, vous constaterez qu'elle produit le même résultat à chaque fois. Ceci est dû au fait que la fonction resulting-symbol
en el let
est produite lors de la phase de compilation. Compiler une fois à l'avance signifie que cette valeur est intégrée dans le fichier .jar. Ce que nous voulons vraiment faire, c'est pousser l'évaluation de doc
au moment de l'exécution. Réécrivons donc eval-doc
en tant que fonction.
(defn eval-doc
[sym]
(eval `(doc ~sym)))
C'est aussi simple que cela.