clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
slipset 2020-06-03T05:25:18.200500Z

So, with the link you posted above, would eg compiling all my deps (and not my source code) help on startup time?

orestis 2020-06-03T05:49:06.202800Z

@slipset yes, we did this and saw improvement.

slipset 2020-06-03T06:02:54.204700Z

Thanks! I’ll check that out. I guess my next, naive, question would be: Is there a way to compile a jar?

2020-06-03T17:31:33.206800Z

Tried this out in both java8 and java11 with clojure 1.10.1 and [clj-commons/pomegranate “1.2.0”] - and find the behavior hard to explain - in the repl - using lein 2.9.3. Wonder if anyone has any deeper insight on the classloader trickery happening: ---- These DO NOT work. hickory isn’t found on classpath. (1)

(p/add-dependencies
 :coordinates '[[hickory "0.7.1"]]
 :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                      pa/maven-central))

(require 'hickory.core)
(2)
(p/add-dependencies
 :classloader (clojure.lang.RT/baseLoader)
 :coordinates '[[hickory "0.7.1"]]
 :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                      pa/maven-central))

(require 'hickory.core)
(3)
(def cl (clojure.lang.RT/baseLoader))

(p/add-dependencies
 :classloader cl
 :coordinates '[[hickory "0.7.1"]]
 :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                      pa/maven-central))

(require 'hickory.core)
(4)
(let [cl (clojure.lang.RT/baseLoader)]
  (p/add-dependencies
   :classloader cl
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central)))

;; NOTE: NOT INSIDE the `let`
(require 'hickory.core)
However, this DOES work: (5)
(let [cl (clojure.lang.RT/baseLoader)]
  (p/add-dependencies
   :classloader cl
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))
  ;; NOTE: INSIDE the `let`.
  (require 'hickory.core))
Also, after (5) happens in the repl, you can just use the new dependencies with no further let-bindings or references to that classloader. I think I somewhat understand why at this point since the dynamic classloaders are likely just sharing state of these clj loaded classes.

2020-06-03T17:32:27.207300Z

it’s pretty weird to me. The case where the classloader is in a let-binding seems to matter. It makes it “used by require

2020-06-03T17:32:41.207600Z

but no other time. Notably, not even a var holding the loader matters (case (3))

2020-06-03T17:52:37.209500Z

I would try #2 with both expressions in a let with no bindings

2020-06-03T17:52:44.209700Z

(let [] ...)

2020-06-03T17:54:21.211Z

I forget the exact details but when evaluating/compiling code the compiler will often push a new dynamic classloader binding, which is sometimes what RT/baseLoader returns

2020-06-03T17:55:22.212200Z

so my guess is what you are seeing is differences in behavior based on if the add-dependencies expression is compiled and run with the same classloader on that stack as the require expression

2020-06-03T17:55:48.212400Z

ahhh ok

2020-06-03T17:55:55.212600Z

I think what your saying makes sense

2020-06-03T17:56:08.213200Z

I wasn’t thinking enough about clojure.lang.RT/baseLoader returning different things

2020-06-03T17:56:45.214Z

you’r probably rigth I’m getting the wrong case

2020-06-03T17:57:12.214500Z

not 100% sure, wrapping in a let like that will make the two part of the same compilation unit, without all the explicit classloader stuff, so if that fixes the issue it is likely what is happening

2020-06-03T17:57:48.215400Z

yeah, and perhaps I should use clojure.lang.RT/makeClassLoader as well to test

2020-06-03T17:57:54.215700Z

which would always give me a dynamic loader

2020-06-03T17:57:56.215900Z

regardless of baseloader

2020-06-03T17:58:13.216800Z

I doubt that will do anything

2020-06-03T17:58:20.217100Z

but the compilation unit binding Compiler.LOADER is a prime suspect

2020-06-03T17:58:54.217800Z

the issue isn't likely with what you are explicitly passing to pomegranate, but what the compiler is implicitly doing behind the scenes

2020-06-03T17:59:50.218700Z

I've had to fiddle with requires with pomegranate in the past(the behavior changed around clojure 1.8 I think), but never dug into why

2020-06-03T18:00:09.219Z

so doing case (2) in a let-binding as you said works

2020-06-03T18:00:12.219200Z

as expected

2020-06-03T18:00:22.219400Z

eg:

(let []
  (p/add-dependencies
   :classloader (clojure.lang.RT/baseLoader)
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))

  (require 'hickory.core))

2020-06-03T18:00:32.219700Z

Then my repl loader permanently can refer to these new classes after too

2020-06-03T18:00:35.219900Z

(as before)

2020-06-03T18:02:05.220800Z

and

(p/add-dependencies
 :classloader (clojure.lang.RT/makeClassLoader)
 :coordinates '[[hickory "0.7.1"]]
 :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                      pa/maven-central))
(require 'hickory.core)
does not work; as you assumed above - I’m still not sure I can reason why it doesn’t work

2020-06-03T18:02:17.221Z

oh yes, I think I can

2020-06-03T18:02:32.221400Z

require is just not occurring in a compilation unit that would know of this new one

2020-06-03T18:03:32.222400Z

So basically, if we can get “loading” (eg. require) to happen in the same compilation unit as using clojure.lang.RT/baseLoader to add the deps; we’ll be dealing using that same mutated loader from pomegranate

2020-06-03T18:03:54.222800Z

I think once we do the load; it is safe to just use the stuff after that point within the repl

2020-06-03T18:04:21.223200Z

meaning, I don’t know of a reason I’d need to hold onto the loader and keep binding it

2020-06-03T18:12:39.223500Z

to close it out

2020-06-03T18:12:41.223700Z

you can also do

2020-06-03T18:12:46.223900Z

(def cl (clojure.lang.RT/makeClassLoader))
(with-bindings {clojure.lang.Compiler/LOADER cl}
  (p/add-dependencies
   :classloader cl
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))

  (require 'hickory.core))

2020-06-03T18:13:19.224300Z

then again, using with-bindings may be making them both in the same compilation unit still

2020-06-03T18:13:40.224800Z

so probably should break all the way into push thread bindings around multiple separate forms to see it in most raw

2020-06-03T18:14:22.225500Z

no, I doubt you need to do any explicit classloader stuff at all

2020-06-03T18:14:31.225900Z

(def cl (clojure.lang.RT/makeClassLoader))
(with-bindings {}
  (p/add-dependencies
   :classloader cl
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))

  (require 'hickory.core))

2020-06-03T18:14:32.226100Z

fails

2020-06-03T18:14:38.226300Z

so it does get compiled in different context

2020-06-03T18:14:51.226700Z

stop passing in a classloader

2020-06-03T18:15:02.226900Z

fails too

2020-06-03T18:15:13.227200Z

(with-bindings {}
  (p/add-dependencies
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))

  (require 'hickory.core))

2020-06-03T18:15:14.227400Z

fail

2020-06-03T18:15:16.227500Z

wrap it in let instead

2020-06-03T18:15:20.227800Z

I get let works

2020-06-03T18:15:28.228100Z

I’m trying to show the underlying mechanism of why let works

2020-06-03T18:15:44.228600Z

the sharing of the clojure.lang.Compiler/LOADER

2020-06-03T18:15:59.229Z

(let []
  (p/add-dependencies
   :coordinates '[[hickory "0.7.1"]]
   :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                        pa/maven-central))

  (require 'hickory.core))
doesn’t work though

2020-06-03T18:16:02.229200Z

you have to refer to baseloader

2020-06-03T18:16:06.229600Z

don’t think pomegranate does (by default)

seancorfield 2020-06-03T19:58:44.230700Z

@mikerod This seems to be all accidental complexity introduced by pomegranate -- using add-lib for this sort of thing "just works".

2020-06-03T20:01:12.231400Z

@seancorfield add-lib being an unreleased fn right?

seancorfield 2020-06-03T20:01:27.231800Z

(! 504)-&gt; clj -A:deps -r
user=&gt; (require '[clojure.tools.deps.alpha.repl :refer [add-lib]])
nil
user=&gt; (add-lib 'hickory {:mvn/version "0.7.1"})
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See <http://www.slf4j.org/codes.html#StaticLoggerBinder> for further details.
true
user=&gt; (require 'hickory.core)
nil
user=&gt; 

seancorfield 2020-06-03T20:02:03.233Z

:deps ; to get access to clojure.tools.deps.alpha.repl/add-lib
  {:extra-deps {org.clojure/tools.deps.alpha
                {:git/url "<https://github.com/clojure/tools.deps.alpha>"
                 :sha "19d197ab221d37db750423eb970880cb87a91100"}}

dominicm 2020-06-03T20:02:06.233200Z

@seancorfield they use similar strategys. This doesn't just work with add-lib when using lein/add-lib

2020-06-03T20:02:26.233900Z

yeah pomegranate isn’t really doing much fancy here either

dominicm 2020-06-03T20:02:28.234100Z

There's a long outstanding cider issue about this.

dominicm 2020-06-03T20:02:37.234500Z

I think it recently got closed

2020-06-03T20:02:40.234900Z

just trying to add to a classloader when it has the ability to be added to - via a protocol

dominicm 2020-06-03T20:02:56.235600Z

I'd love to know what the deal is with COMPILER, because that stumped me.

2020-06-03T20:03:09.235900Z

COMPILER?

dominicm 2020-06-03T20:03:22.236500Z

Sorry, LOADER. hazy memory

2020-06-03T20:03:29.236800Z

It makes sense above

2020-06-03T20:03:33.237100Z

but it’s sort of digging

2020-06-03T20:03:39.237500Z

you can do similar with the context classloader instead

2020-06-03T20:03:53.238500Z

but you are somewhat relying on the fact that clj require (`load`) will use a RT/baseLoader

seancorfield 2020-06-03T20:04:04.239200Z

So the problem here is... what exactly? I'm confused. You show something related to Leiningen / Pomegranate that doesn't work in situations you think it should... and that's a bug/problem in... what? I'm not following...

2020-06-03T20:04:08.239400Z

which will in turn use the bound LOADER or context loader if you set that etc

dominicm 2020-06-03T20:04:14.239700Z

But why does lein have particular issues with LOADER. I couldn't get it to trigger under clojure.

seancorfield 2020-06-03T20:04:37.240600Z

Right, this is feeling like a lein/CIDER problem, not a Clojure problem.

2020-06-03T20:04:41.240800Z

It’s just that in some repl’s the classloaders are new each eval

2020-06-03T20:04:43.241Z

or something to that extent

2020-06-03T20:04:53.241400Z

so if you use something like pomegranate incorreclty - you may mutate the wrong loader

2020-06-03T20:05:05.242Z

and not really have ability to use that loader for something like require subsequently

2020-06-03T20:05:11.242400Z

above there is outlined approaches for that to not be the case

2020-06-03T20:05:25.243Z

it’s something that happens in lein, not really a “problem”

seancorfield 2020-06-03T20:05:31.243400Z

So that's... a bug in lein?

2020-06-03T20:05:35.243700Z

just something you sort of have to understand if mutating a loader

dominicm 2020-06-03T20:05:38.243900Z

Hmm. I fixed that in nrepl though

2020-06-03T20:05:44.244300Z

you have to be sure you are using that mutated loader

dominicm 2020-06-03T20:05:44.244400Z

I gave them a common base.

seancorfield 2020-06-03T20:06:32.245Z

(sorry, I'm not trying to be difficult, I'm just trying to see how this relates to #clojure-dev )

2020-06-03T20:07:12.245700Z

I wanted to understand the underlying mechanism for why a given compile-context seemed to change my result

2020-06-03T20:07:17.245900Z

I don’t care that I was using a lein repl to get to that state

2020-06-03T20:07:22.246100Z

so to me - relates directly

2020-06-03T20:07:44.246500Z

you can have different repl impl’s some may or may not end up the same - depends on how they manage classloaders etc

2020-06-03T20:07:57.246900Z

I’m not saying there is even a problem, was just baffled by how let seemingly caused a difference for no reason

2020-06-03T20:08:13.247300Z

and conclusion was: it formed a “single compilation unit”

2020-06-03T20:08:18.247500Z

where the Compiler.LOADER was bound

2020-06-03T20:08:28.248100Z

which affects the clojure.lang.RT/baseLoader return val

2020-06-03T20:08:55.249100Z

could perhaps have been find in #clojure channel

2020-06-03T20:09:06.249700Z

hard to say when it’s pretty internal-digging

dominicm 2020-06-03T20:09:10.249900Z

Why does that change the base loader? That's been my question for years :)

2020-06-03T20:09:24.250100Z

static public ClassLoader baseLoader(){
	if(Compiler.LOADER.isBound())
		return (ClassLoader) Compiler.LOADER.deref();
	else if(booleanCast(USE_CONTEXT_CLASSLOADER.deref()))
		return Thread.currentThread().getContextClassLoader();
	return Compiler.class.getClassLoader();
}

2020-06-03T20:09:30.250300Z

first if

2020-06-03T20:10:32.251200Z

then you can see in clojure.lang.Compiler.eval() how this LOADER is bound

2020-06-03T20:11:11.252700Z

so if you do some mutation to it - via pomegranate and also end up doing a load while still in that same eval - you are working against same loader at the right time

2020-06-03T20:11:13.252900Z

likely the reason lein's repl behaves different is the clojure.main/repl sets the context class loader to be a dynamic classloader

2020-06-03T20:11:38.253500Z

don’t have to rely on let though - you can just use with-bindings ; you also don’t even need LOADER; you can set the USE_CONTEXT_CLASSLOADER and achieve similar

2020-06-03T20:12:03.254100Z

Yeah, never looked into why lein does whatever it does with loaders

2020-06-03T20:12:15.254400Z

lein likely does nothing

2020-06-03T20:12:33.254700Z

and it isn't really on lein

2020-06-03T20:12:47.255100Z

the issue is with the server side of the nrepl it creates

seancorfield 2020-06-03T20:15:57.257200Z

FWIW, of your original five cases, (1) does work in the regular clj REPL (and it also worked for me in a plain lein repl) (2), (3), and (4) do not work, and (5) does work. -- @mikerod Does (1) not work for you in lein repl?

2020-06-03T20:16:33.258200Z

yeah, the server is really from nrepl

2020-06-03T20:16:49.259200Z

and it does have middleware setting the ccl and LOADER etc - so things are being done

2020-06-03T20:17:04.259600Z

it likely all depends on the mix of nrepl and clojure versions being used

seancorfield 2020-06-03T20:17:13.260Z

If (1) works for you in lein repl, then I'm much clearer about what you're asking -- which is about the (2), (3), and (4) cases which don't work in either REPL and so that points to an isolated class loader issue (am I close / caught up now?)

2020-06-03T20:17:27.260200Z

@seancorfield all above was for lein repl

2020-06-03T20:17:38.260700Z

and (1) was in the “doesn’t work” list

2020-06-03T20:17:51.261200Z

so whatever is happening in the repl there - which really relates to nrepl - didn’t end up sharing the loader across forms

2020-06-03T20:18:13.261800Z

perhaps do could work, but think that may be split

seancorfield 2020-06-03T20:18:13.261900Z

Ah, so you have some middleware/plugin that is breaking (1) in lein repl (as well as the three common cases)...

2020-06-03T20:18:18.262100Z

so have to use let or something to keep it grouped

2020-06-03T20:18:37.262500Z

yes, many variables involved concerning what repl is used and what that repl does

2020-06-03T20:19:07.263200Z

And again: I posted the examples trying to just get an idea of what mechanism was causing the the weird difference I saw when using let.

2020-06-03T20:19:15.263500Z

I’m not reporting like a bug or issue - to be clear

2020-06-03T20:19:30.263800Z

was exploratory

1
2020-06-03T20:19:49.264400Z

good news is - it makes sense how to make pomegranate work now in lein repl’s (to me at least)

2020-06-03T20:20:08.264800Z

I think add-lib will hit similar situations in lein

2020-06-03T20:20:18.265100Z

looks to do something quite similar with how it mutates the loader

seancorfield 2020-06-03T20:21:27.266300Z

I haven't used lein for much of anything since late 2015, so I was just very puzzled when I tried your example (1) in a plain lein repl and it worked... hence my confusion! 🙂

2020-06-03T20:21:45.266500Z

hmm weird if it worked

2020-06-03T20:21:54.266800Z

but yeah, I know you seem to prefer the alt stack

seancorfield 2020-06-03T20:23:26.267800Z

(! 513)-&gt; lein version
Leiningen 2.9.3 on Java 14 OpenJDK 64-Bit Server VM
(! 514)-&gt; lein repl
nREPL server started on port 52427 on host 127.0.0.1 - <nrepl://127.0.0.1:52427>
REPL-y 0.4.4, nREPL 0.6.0
Clojure 1.10.0
OpenJDK 64-Bit Server VM 14+36-1461
Just so it's clear what version(s) I tried it on. No project.clj present.

dominicm 2020-06-03T20:31:40.268300Z

@mikerod next version of nrepl has a workaround for this

2020-06-03T20:34:38.268700Z

Same as me @seancorfield besides I tried java8 & 11 (but those shouldn’t change the loader setup of the nrepl server)

2020-06-03T20:34:47.269Z

@dominicm interesting; I’ll have to see - the current repl still can work as is - just have to be more careful about things, but would be nice to not have to

2020-06-03T20:35:16.269300Z

still no idea why Sean’s is working haha

2020-06-03T20:35:56.269600Z

not for this case:

(p/add-dependencies
 :coordinates '[[hickory "0.7.1"]]
 :repositories (merge {"clojars" "<https://repo.clojars.org/>"}
                      pa/maven-central))

(require 'hickory.core)
with that version of lein

2020-06-03T20:36:45.270200Z

the issue is the version of nrepl, the lein version is a red herring

2020-06-03T20:36:52.270400Z

oh, guess I somewhat cut off too [cemerick.pomegranate.aether :as pa] for the maven repo

2020-06-03T20:37:12.270800Z

@hiredman lein version directly chooses the nrepl version though?

2020-06-03T20:37:15.271Z

it’s part of it’s deps

2020-06-03T20:37:22.271400Z

and I’m using 2.9.3

2020-06-03T20:37:29.271500Z

it is overridable

2020-06-03T20:37:53.272200Z

I’m not overridding

2020-06-03T20:38:01.272700Z

I would double check

2020-06-03T20:38:03.272800Z

unless Sean is or something (doubt it since he doesn’t use lein)

2020-06-03T20:38:56.273600Z

I am not sure how one would do that though, I try deps :tree, but the nrepl version might be part of some profile thing, dunno

2020-06-03T20:39:15.273800Z

yeah, I’m using nrepl "0.6.0" as well

2020-06-03T20:39:22.274Z

in deps tree