I want to display random (doc) page for some namespace.
The random function name I can get by:
user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char
When I try to pass this to (doc) I get this:
user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I’m new to Clojure and I’m not sure how to deal with this… I tried to convert this into regexp and use (find-doc) but maybe there is a better way to do this…
Explanation
The problem here is that
docis a macro, not a function. You can verify this with thesourcemacro in the repl.If you’re new to Clojure (and lisps), you might not have encountered macros yet. As a devastatingly brief explanation, where functions operate on evaluated code, macros operate on unevaluated code – that is, source code itself.
This means that when you type
docattempts to operate on the actual line of code –(rand-nth (keys (ns-publics 'clojure.core)))– rather than the evaluated result (the symbol this returns). Code being nothing more than a list in Clojure, this is why the error is telling you that a list can’t be cast to a symbol.Solution
So, what you really want to do is evaluate the code, then call
docon the result. We can do this by writing another macro which first evaluates the code you give it, then passes that todoc.You can pass
eval-docarbitrary forms and it will evaluate them before passing them todoc. Now we’re good to go.Edit:
While the above works well enough in the repl, if you’re using ahead ahead-of-time compilation, you’ll find that it produces the same result every time. This is because the
resulting-symbolin theletstatement is produced during the compilation phase. Compiling once ahead of time means that this value is baked into the .jar. What we really want to do is push the evaluation ofdocto runtime. So, let’s rewriteeval-docas a function.Simple as that.