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

+1 vote
in REPL by
edited by

For various reasons, I want to start a REPL via the -e eval-opt, but add-lib doesn't seem to work as expected in that case -- well, it loads the libs, but they aren't available to the classloader in the REPL. Here's the smallest repro case I could figure out:

> clj -M -e "((requiring-resolve 'clojure.main/main))"
Clojure 1.12.0
user=> (add-lib 'hiccup/hiccup)
[hiccup/hiccup]
user=> (require '[hiccup.core :as h])
Execution error (FileNotFoundException) at user/eval5 (REPL:1).
Could not locate hiccup/core__init.class, hiccup/core.clj or hiccup/core.cljc on classpath.
user=>

I assume it's a classloader issue and I'm wondering if there's a workaround.

The broader context is https://github.com/seancorfield/dot-clojure/blob/develop/src/org/corfield/dev/repl.clj which is my function to start various combinations of REPLs, dynamically depending on what libraries are on the classpath.

I've never previously noticed this weirdness because I've only ever using add-lib etc via an nREPL server connection started by the above code, where it works just fine (run add-lib via nREPL, then require the new ns via nREPL -- works -- and then it can also be required in the interactive REPL). It would be nice to have it work purely interactively as well...

by
edited by
Hi Sean,

Not an answer, but I can reproduce it too, and just for reference I think there is a typo in your example:

`(require '[hiccup.core :as h])`

rather than `'[hiccup. Core :as h]`.
by
Dang auto-correct/spellcheck thing! Fixed in my Question. Thanks.

1 Answer

0 votes
by
selected by
 
Best answer

Definitely is classloader related. I assume the difference is that clojure.main/repl does:

(let [cl (.getContextClassLoader (Thread/currentThread))]
  (.setContextClassLoader (Thread/currentThread) (clojure.lang.DynamicClassLoader. cl)))

and clojure.main/eval-opt does not.

Seems like your code could do that too.

by
Ah, I used to have that code in there at one point -- to support DCL for socket REPLs, which I no longer use. Will experiment and let you know (although it won't solve the minimal OP, which would require that code be added to Clojure itself).
by
Nope, that doesn't fix it, even with my more complex example setup. I tried setting the DCL in several places in the pipeline of calls and none of them have any impact on the problem. Seems like it would need a change in Clojure at this point?
by
Is there some reason -e is important here vs running a main with -m?
by
The reason I ask is that -e is a one-off single expression runner, I don't know that it is a reasonable expectation that you can do literally anything with it and have it work the same as other runtime contexts, so I'm trying to tack back towards understanding your actual goal of running a custom repl to consider what's important. add-lib relies on being in the scope of a top-level DynamicClassloader which can dynamically have a new URL class source. Some entry point contexts have that, some don't. I'm trying to tease these things apart into what's important. I have spent some time on this more general issue in the as yet uncompleted https://clojure.atlassian.net/browse/CLJ-2540  (see the helper macro in the patch there for example).
by
I don't remember the original reason for using -e here instead of -m

However, even with -m in my more complex setup, I still can't _quite_ get the seamless behavior I would like -- but I am a little bit closer: the original question, as asked, now works correctly that way.

I can't use add-lib in one context (nREPL or interactive REPL) and then just require in the other context -- I have to also do a require in the context where add-lib was called, but after that it is available in both contexts.

That's good enough for me for now, so I'll deem this "answered". My complex setup, where my dev.repl (main) calls nREPL's main which in turn calls Rebel Readline's main is... strange enough that I'll accept the need to explicitly require newly-added libs in that same context.
...