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

+1 vote
in Compiler by

Why can't I use require in a function like so?

(comment
  (->> [1 2]
       (reduce (fn [acc v]
                 (require '[clojure.set :as c-set])
                 (c-set/union acc #{v}))
               #{})) ;=> Syntax error compiling at (lab/sandbox_playground.clj:31:18).
                     ;   No such namespace: c-set
  )

At first I was thinking it was that require needs to go top-level. However: This does work:

(comment
  (do
    (require '[clojure.set :as c-set])
    (c-set/union #{1} #{2})) ;=> #{1 2}
  )

2 Answers

+3 votes
by
selected by
 
Best answer

Why not just use requiring-resolve?

Edit, for more detail:

(comment
  (->> [1 2]
       (reduce (fn [acc v]
                 ((requiring-resolve 'clojure.set/union) acc #{v}))
         #{}))
  ;;=> #{1 2}
  )

As I learned talking to Peter (op) offline, the reason that specific piece of code doesn't work is named the Gilardi scenario. An extended description of the problem is available at ClojureDocs.

Another reason to prefer requiring-resolve when possible is that require is not thread-safe as of Clojure 1.11.1. More details about this can be found (among others) in this question, and on ClojureDocs here and here.

by
Yes, why not? Can you elaborate some?
by
(comment
      (->> [1 2]
           (reduce (fn [acc v]
                     ((requiring-resolve 'clojure.set/union) acc #{v}))
             #{}))
      ;;=> #{1 2}
      )
by
reshown by
One more note: tools.build uses requiring-resolve heavily, so I consider that as the official solution for things like this unless the semantics of that fn aren't suitable for the case at hand https://github.com/clojure/tools.build/blob/a9d0804ce8014088a7388e762034e9013922cacb/src/main/clojure/clojure/tools/build/api.clj#L126
+1 vote
by
edited by

I tried this:

(defmacro require-macro [arg]
  (require arg))

(->> [1 2]
 (reduce (fn [acc v]
           (require-macro [clojure.set :as c-set])
           (c-set/union acc #{v}))
         #{}))

I think when the lambda is read / compiled, it is not executed. To execute at compile-time, maybe you can invoke a macro 'function'.

by
That actually works. Why, I don't understand though. Can you explain?
by
When the lambda definition is compiled, as a function, it has been executed 0 times. Therefore, the require has not been done yet, when at the second line it is already used. (And we are still at compile-time.) The compiler needs to know what 'c-set' is.
by
I think I understand things that far. But I don't understand why wrapping the require in a macro makes the use of `c-set` work.
by
The only difference is that it is executed at macro-expansion time. (A macro is a function usually from code to code, but in this case, it is used for the side effect of invoking require.) It is probably not a solution to your problem if you want to build an AOT compiled library with such definitions. It works at the REPL thow.

Note: I don't quite understand your final use case.
by
Thanks. I guess I need to go think a bit of when macros expand. =)

My use case is in ClojureScript (or, rather SCI in a nodejs environment)  where I need to postpone a require until a promise resolves. I have yet to try this solution there, but will do that and report back how I fare. Right now I am using @Imre's solution, and that is fine for my use case, but I'd still want to try this one out.
by
Sorry, but my hack with a pseudo-macro will not be useful in this case, even thow it gives the good result in the REPL. Imre is right.
by
It actually does work in my use case. FYI.  (I'll stick to Imre's solution, but anyway.)
...