Welcome! Please see the About page for a little more info on how this works.

+1 vote
in ClojureScript by

Problem statement

Authors writing a macro for clojurescript don't have a facility for resolving symbols to vars. That is: to expand at macroexpansion time a symbol to its fully qualified form:

+     ;; -> cljs.core/+
s/def ;; -> cljs.spec.alpha/def

In the JVM, this is certainly a possibility.

In cljs, there's no such possibility, at least cleanly.

  • There is the cljs.core/resolve macro, but I'd say that it is aimed to be used in cljs final code, not in macros targeting cljs.
    • Specifically, cljs.core/resolve expects a quoted symbol, which is an unnecessary restriction for macro authors.
  • cljs.core/resolve returns a new var, not the original var. This drops user-provided metadata.
    • Macro authors, consequently, cannot observe user-provided metadata for performing macroexpansion-time custom logic

Request

Make cljs.core/resolve more flexible, or create a separate resolving function specifically aimed at macro authors

Use case

I had some existing JVM macros, using resolve at macroexpansion time that needed a port to cljs. i.e. this request comes from a practical need.

Workaround

The following function works fine, satisfying the described requirements.

However it's reasonable to think it's playing with cljs internals, and that accordingly it may break with new cljs releases.

(defn cljs-resolve
  [env sym]
  (let [[var meta] (try
                     (let [var (cljs.analyzer/resolve-var env sym (cljs.analyzer/confirm-var-exists-throw))]
                       [var (cljs.analyzer/var-meta var)])
                     (catch Throwable t
                       [(cljs.analyzer/resolve-var env sym) nil]))]
    (some-> var
            :name
            (vary-meta assoc :cljs.analyzer/no-resolve true)
            (vary-meta merge meta))))

2 Answers

+1 vote
by

What is your use-case and why is cljs.anaylzer/resolve-var not suitable?

cljs.analyzer/resolve-var should provide everything you need. It returns a map with all the info available from the analyzer. It does have a :meta key. Not sure why you are going through all the other stuff in your impl.

cljs.core/resolve really is just a helper added for code-splitting and even its use there is questionable, don't use it. It is ok to reach into cljs.analyzer/resolve-var in macros.

by
Thanks Thomas, I will make sure to check out `resolve-var`. I wasn't aware of it and for some reason was under the impression that `cljs.core/resolve` was the only thing being offered.
by
Thought you were aware of it because the "Workaround" example uses it. cljs.analyzer/resolve-var can be called with the macro &env and a symbol.
by
Sorry, yes indeed I use (and therefore was aware of) `resolve-var`.  I had gotten the impression that this was an implementation detail; I guess that's not the case?

I recall finding `resolve-var` by peeking at the source, not by finding it in any documentation.
0 votes
by

Have you looked at cljs.analyzer.api/resolve? It's preferred over calling cljs.analyzer/resole-ar.

...