Fork me on GitHub

I have a gen-class in namespace A that calls a class from gen-class from namespace B. Would it make sense that I need to compile namespace B first before I compile namespace A, or can clojure resolve this by itself? I'm asking because that class in namespace B is a return value of a method in namespace A and I get error message that the class isn't found (but could be unrelated).


Do you require namespace B from A?


nevermind, I made a mistake, works fine


(let [do 'do] (do {'do 1})) ;;=> '{do 1}
Should the result of this not be 1?


cf. (let [x 'do] (x {'do 1})) ;;=> 1


looks fine to me?


there's a ticket in jira


oh yeah it's been closed as dupe


ah, looks like something with special forms not being shadowed by local bindings


like that: (let [if :a] (if {:a 1})) => Too few arguments to if


I ran into this while fixing a bug in sci


and then wondered: what would the real Clojure do


oh yeah your example is indeed a bit different than the ticket I linked @borkdude


it's by design that special forms in call position are resolved before locals, so your first example is correct


CLJ-1216 has to do with do being used as a local in value position rather than call position, so ignore it


reason for special forms having higher precedence than locals: imagine (let [do 1] (some-macro whatever))


because special forms are not namespace qualified nor gensym'd in syntax-quote, if locals had higher precedence that code would break if some-macro used do


makes sense


this would not be necessary if they were namespace qualified but alas, too late to change that


not a big deal, one probably should avoid choosing special form/clojure var names as locals anyway

Vasudevan Comandur15:11:10

I am facing syntax error issue while compiling my project using clojure version 1.10.1 whereas the same project was compiling successfully in Clojure version 1.8.0.


what's the error?

Vasudevan Comandur15:11:14

Just says Syntax error.


anything else?.. stacktrace? saying there is full report in some tmp file?

Vasudevan Comandur15:11:43

I shall attach the stack trace

Vasudevan Comandur15:11:38

I added print statements in LispReader.Java file. You can see the scanned tokens in the beginning


it says ClassNotFoundException: clojure.lang.RT


sounds strange, if you have lisp reader, you would guess you should have RT as well

Vasudevan Comandur15:11:08

That class is part of clojure.jar file is in it?

Vasudevan Comandur15:11:56

It started reading the first line of nlplabs/webfetch/lib.clj file


can you start a normal repl without graal?

Vasudevan Comandur15:11:42

after consuming all the characters it was throwing the error


Clojure 1.9 and later do more checking of the contents of ns forms than Clojure 1.8 did.


It might be easier for others to spot the error in that ns form if you could copy and paste it here.

Vasudevan Comandur15:11:21

Yes. To start REPL in clojure 1.10.1, it was requiring spec.alpha-0.2.176.jar to start successfully


@UQHJGGW86 How are you starting your REPL? Are you not using lein/`boot` or the new CLI with deps.edn?


Never mind, I just saw that you're calling Clojure from inside a Grails app running as a WAR on Tomcat. Certainly a very unusual execution environment.


I know it is there, one character per line, but a bit tedious to read it that way.

Vasudevan Comandur15:11:01

Here it is (ns nlplabs.webfetch.lib (:refer-clojure)) (:use nlplabs.lib))


yeah, but as I understood from output, ns declaration is fine here: (ns nlplabs.webfetch.lib (:refer-clojure) (:use nlplabs.lib))


wait, there is unnecessary closing paren after refer-clojure


The beginning of the debug output makes it look like the file maybe begins with ((. Is that me misinterpreting that output?

Vasudevan Comandur15:11:09

In clojure version 1.8, the lisp reader started processing the files

Vasudevan Comandur15:11:38

Sorry, it was not there. I was just testing it but forgot to remove


Using unmodified Clojure 1.10.1 will probably give a semi-clearer error, vs. using your modified version, if that is easy for you to do.


But I will try out that ns form locally on my machine that way to see what it says.

Vasudevan Comandur15:11:29

In Clojure 1.8, lisp reader was processing the files


Clojure 1.10.0 gives no error when loading that ns form


It would be easier to help if you can reproduce a problem and error message with an unmodified Clojure 1.10.1

Vasudevan Comandur15:11:25

in REPL 1.10.1, I had tried the same and I did not get any error.


So you only encounter a problem when trying to load that code with your locally modified version of Clojure 1.10.1 ?

Vasudevan Comandur15:11:48

When I build my project, by calling compile.load using the project, I am getting the error


Out of curiosity, why are you using compile.load, instead of more common methods of loading or compiling Clojure code?


e.g. clojure.core/compile can also be used to AOT compile Clojure


and clojure.core/require or load-file can be used for loading it.


Maybe you are doing this because you want to load Clojure code and call it from a Java program, or some other JVM language?

Vasudevan Comandur15:11:47

Our project has been compiled in this mode since clojure 1.2

Vasudevan Comandur15:11:05

It was working fine till 1.8

Vasudevan Comandur15:11:30

We thought of migrating 1.10.1 (stable version).

Vasudevan Comandur15:11:42

started encountering this error.


Do you have a pointer to the compile.load method you are using, in the Clojure source code? I'm not familiar with it. Your best bet might be to ask in #clj-dev channel where folks knowing the APIs for invoking the Clojure compiler are more common, but if so, you should be prepared to provide as much detail as you can, e.g. code snippets showing exactly what calls were working in Clojure 1.8 and no longer working in 1.10

Vasudevan Comandur15:11:41

As you can see in the stack trace, 7648 | load in clojure.lang.Compiler is called

Vasudevan Comandur15:11:05

It is loading the lib.clj file and when lispReacer is processing the file, it is throwing the syntax error.


Is there a short Clojure expression you used that you can share, that leads to this stack trace?


I don't know what code you executed that caused this to happen -- I may have missed it earlier.


There are any of dozens of Java methods inside the Java implementation you might have tried calling directly, or something else. I looked back at earlier messages, and I do not think you have said yet how you are initiating this process.


I will be away from keyboard for next hour or so, but will look at any messages you have after returning. Again, if you can explain exactly what steps you follow that lead to your problem, with an unmodified Clojure 1.10.1, maybe someone in #clj-dev can help better than I can.

Vasudevan Comandur15:11:43

Sure Andy. Thanks for your support.

Vasudevan Comandur15:11:40

I am able to get the all the clj code in nlplabs.lib successfully in REPL with lispReader spitting the output

Vasudevan Comandur15:11:33

Here is the project environment:

Vasudevan Comandur16:11:46

we are building an aggregator which will access multiple job portals. we are using Grails framework to build the app and run under tomcat. We create a war file of the app and deploy under tomcat. The clojure files are also bundled in the WAR package. During the deployment, clj files are compiled and the app will be running under tomcat.

Vasudevan Comandur16:11:49

We are trying to migrate the app which has clojure component to be migrated to 1.10.1 from 1.8.0

Vasudevan Comandur16:11:52

Now, I was trying to deploy my app after copying necessary clojure 1.10.1 JAR files in the lib folder.

Vasudevan Comandur16:11:03

I am facing this problem.

Alex Miller (Clojure team)16:11:12

Clojure 1.10.1 changed the RT initialization process - it's possible that this broke some assumption of how the Clojure runtime is being loaded

Alex Miller (Clojure team)16:11:10

My impression from the stack above is that this code is not using any public documented API to initialize the runtime

Alex Miller (Clojure team)16:11:20

so could easily be affected

Alex Miller (Clojure team)16:11:50

if you could easily try with Clojure 1.10.0 (and it worked) then I would strongly guess this is the cause

Alex Miller (Clojure team)16:11:30

but it could also be something to do with the addition of spec macro checking to the initialization path or something else. that GClojure class seems like a key piece of code to review

Vasudevan Comandur16:11:31

Alex, I just now brought up 1.10.1 in REPL mode and gave the first line (ns nlplabs.webfetch.lib (:refer-clojure) (:use nlplabs.lib)) and it went through fine

Vasudevan Comandur16:11:07

processed all the files in nlplabs.lib successfully


We are guessing that some part of your code is calling into some Java method in the implementation of Clojure that was never documented as "we promise not to change that", and it changed.


For example, this is the (very small) Java API that they promise to support:

Vasudevan Comandur16:11:33

The lib.clj definition is as below(ns nlplabs.lib (:refer-clojure) (:use clojure.test) (:require (nlplabs.lib defs date file logging readermacro locals re signal)))


If you call some other Java methods, then from one version of Clojure to the next, they might change.


I don't suspect that ns form has any syntax errors, as I was guessing earlier, because I tried that on Clojure 1.10.1, too, and it gives no errors. The problem is in how you are invoking the Clojure compiler, is my current best guess.

Vasudevan Comandur16:11:29

I am copying the code snippet of calling clojure compiler.load public static void loadExtractorLib() throws Exception { synchronized (GClojure.class) { if (loadedExtractors) return; loadedExtractors = true; } load("nlplabs/webfetch/lib.clj"); } public static void load(String resource) throws Exception { InputStream stream = LFUtil.openResourceStream(resource); if (stream != null) { try { File f = new File(resource); Compiler.load(new InputStreamReader(stream, "UTF-8"), resource, f.getName()); f = null; } finally { try { stream.close(); } catch (IOException ignored) { } } } }


Your stack trace mentions a class com.nlplabs.util.GClojure. Is that code owned/maintained by your organization, or is it some third party library that you use?

Vasudevan Comandur16:11:08

I just copied the snippet from fiile which calls the compiler.load with nlplabs/webfetch/lib.clj


I see it, and anyone who can help you resolve the issue would likely want to see that. Sorry, but I don't know off top of my head the changes in this area that Alex Miller was referring to earlier.

Vasudevan Comandur16:11:45

Thanks Andy for your help & support.

Vasudevan Comandur16:11:34

I am wondering if I need to dig into code under Clojure.lang


As a guess, it might be that adding some call to some initialization method for the RT class somewhere in here might help, but any concrete suggestion from me like that would be guessing and poking at the machine, without full understanding, but there are folks in the #clj-dev channel who understand this better than I. They would want to see the code snippet above, and the stack trace you showed earlier, and that "it worked with Clojure 1.2 through 1.8, but something is different about Clojure 1.10 here"

Vasudevan Comandur16:11:08

FYI, I am using JDK 1.8


Do you know how to join the #clj-dev channel?


sorry, it is called #clojure-dev -- I keep making that mistake.

Vasudevan Comandur16:11:32

I joined this forum after Alex pointing it to me.


Are you using Slack within a desktop browser? If so, you probably see a "Jump to..." box/link near the upper left of your window, just above "All Unreads"


Click on that, type in clojure-dev, press enter

Vasudevan Comandur16:11:14

I did not see clojure-dev option there


When you click on the "Jump to", does a pop-up text box appear? If so, type the letters clojure-dev using your keyboard, then press enter

Vasudevan Comandur16:11:05

I searched and got clojure-dev

Vasudevan Comandur16:11:43

Join channel option is there.


And in the list of "Channels" on the left side of your window, there are separate lines for # clojure and # clojure-dev now?


Each channel is a separate 'discussion room' which can have different participants involved.


#clojure-dev channel tends to have readers who know more about Clojure internals than average.


i.e. Developers of Clojure itself.

Vasudevan Comandur16:11:54

Sure. Thanks for the heads-up

Vasudevan Comandur16:11:11

I am also a beginner

Vasudevan Comandur16:11:42

I shall post this question on dev forum


Sure, you can let them know that, and they'll understand. The stack trace and Java code snippet you gave are key information for them, as is the "works with Clojure 1.2 through 1.8, but not 1.10"

Vasudevan Comandur16:11:29

Thanks Andy once again.


Perhaps they may have a suggestion of changing that Java code in some way.

Alex Miller (Clojure team)16:11:52

I think the key here is that this code is just calling Compiler.load(), which is not a public entry point

Alex Miller (Clojure team)16:11:30

the public way to do this would be to use the Clojure Java API and it's facilities to require clojure.core, then invoke the compile function

Alex Miller (Clojure team)16:11:05

using the API will ensure that the RT (runtime) is properly loaded and initialized

Vasudevan Comandur16:11:04

Here is the import definition of file import clojure.lang.AFn; import clojure.lang.APersistentMap; import clojure.lang.APersistentSet; import clojure.lang.APersistentVector; import clojure.lang.Compiler; import clojure.lang.Keyword; import clojure.lang.RT; import clojure.lang.Var; import com.nlplabs.lf.LFUtil;

Alex Miller (Clojure team)16:11:15

yeah, none of that is public API

Vasudevan Comandur16:11:18

Is anything changed from 1.8 to 1.10.1

Vasudevan Comandur16:11:40

All along the same code was working fine

Alex Miller (Clojure team)16:11:41

that API has not changed, no. as I mentioned above, how RT is initialized has changed

Vasudevan Comandur16:11:24

ok. I shall have a look at the link that you have provided

Alex Miller (Clojure team)16:11:44

what I'm saying is that your code uses internal APIs, which have changed in subtle ways. the public supported API has not changed and has been updated appropriately

Alex Miller (Clojure team)16:11:48

since you are just loading a particular file, rather than compiling a namespace, you might need to go through a few more hoops to do the real equivalent of this

Alex Miller (Clojure team)16:11:01

if you wanted to try just a small change, you could invoke RT.init(); before you call Compiler.load().

Vasudevan Comandur17:11:20

ok. I shall try that.

Alex Miller (Clojure team)17:11:03

That call should exist on any version of Clojure but would just print an error to stderr for recent versions up through 1.10.0. As of 1.10.1, it's actually doing important parts of the initialization.

Alex Miller (Clojure team)18:11:58

obviously, you can't initialize Clojure without the clojure.lang.RT class. This means that either the class is not on the classpath or that the classloader at this point is not able to access it. That's really a JVM/Java question for your application structure, not really a Clojure issue.

Alex Miller (Clojure team)18:11:14

I would check that you have the clojure.lang.RT class on the classpath

Alex Miller (Clojure team)18:11:37

and if so, then start investigating the thread context classloader chain at the point of the load


How can I auto-clear agent errors and keep chugging?


and why might i not want to do that?

Alex Miller (Clojure team)19:11:10

set the error mode when you create the agent

Alex Miller (Clojure team)19:11:21

(agent {} :error-mode :continue}


I have trouble with this macro:

(defmacro gen-constants [& constant-names]
     ~@(for [constant-name constant-names
             :let [full-name (str "java.util.regex.Pattern/" constant-name)
                   full-name-sym (symbol full-name)
                   meta {:bb/export-constant true
                         :bb/full-name full-name
                         :bb/full-name-sym full-name-sym}
                   constant-name (with-meta constant-name meta)]]
         `(def ~constant-name

(gen-constants CANON_EQ)
When I look at the metadata:
user=> (meta #'CANON_EQ)
{:bb/export-constant true, :bb/full-name "java.util.regex.Pattern/CANON_EQ", :bb/full-name-sym 128, :line 1, :column 1, :file "NO_SOURCE_PATH", :name CANON_EQ, :ns #object[clojure.lang.Namespace 0x66bacdbc "user"]}
it seems that :bb/full-name-sym is an evaluated symbol. How do I keep this a literal symbol as metadata on the defined var?


I expect :bb/full-name-sym to be java.util.regex.Pattern/CANON_EQ and not 128

Alex Miller (Clojure team)19:11:04

append # (auto gensym) to symbols that you create and use in the context of backtick

Alex Miller (Clojure team)19:11:21

full-name# instead of full-name

Alex Miller (Clojure team)19:11:13

or maybe I misread since you're already in an unquote there

Alex Miller (Clojure team)20:11:07

yeah, sorry ignore what I said :)


the problem is that the symbol in the metadata, :bb/full-name-sym, gets evaluated


I suspect I have to do something like (list 'quote ...)


that worked


I peeked at the defn macro

Alex Miller (Clojure team)20:11:15

I just did literally that same thing this week with a change in core.async :)

Alex Miller (Clojure team)20:11:30

exact same kind of scenario

Alex Miller (Clojure team)20:11:00

Is there any reason here to actually put the for in the expansion though?


Do you mean (cons 'do (for ...)) instead?


I'm generating multiple defs:


Alex Miller (Clojure team)20:11:50

I mean do the for outside the expansion to construct the data, then emit a (do [email protected])


you mean like:

(defmacro gen-constants [& constant-names]
  (let [defs (for [constant-name constant-names
                   :let [full-name (symbol (str "java.util.regex.Pattern/" constant-name))
                         meta {:bb/export-constant true
                               :bb/full-name (list 'quote full-name)}
                         constant-name (with-meta constant-name meta)]]
               `(def ~constant-name
    `(do [email protected])))

Alex Miller (Clojure team)20:11:06

yes, more like that, although you can take it farther

Alex Miller (Clojure team)20:11:49

have the for instead be a map that takes a seq of constant-names, and builds a seq of full-names


you actually don't need a macro for that, you know

Alex Miller (Clojure team)20:11:15

then (do ~@(interleave (constantly 'def) constant-names full-names)) etc

Alex Miller (Clojure team)20:11:50

this is not right, but you get hte idea


I'm not sure if I find that more readable 🙂

Alex Miller (Clojure team)20:11:41

my main point was really that you shouldn't have the macro emit a bunch of work in the expansion if you can do it in the macro at compile time


I'm not sure if I'm following your line of thought. What does it matter if I write this using for or map, it boils down to the same data at compile time?


I mean:

(defmacro foo [] `(def x '~(for [i [1 2 3]] (inc i))))
(defmacro bar [] `(def x '~(map inc [1 2 3])))
Does that make a difference?

Alex Miller (Clojure team)20:11:09

for vs map doesn't matter (I just prefer to view it as a data transformation pipeline)


yeah, I see what you're getting at, doing things at compile time is cheaper than doing something every time at runtime again and again, but afaik my macro doesn't do very much at runtime

Alex Miller (Clojure team)20:11:53

(defmacro foo [] (let [data (map inc [1 2 3])] `[[email protected]]))

Alex Miller (Clojure team)20:11:14

(macroexpand '(foo))   ;; [2 3 4]

Alex Miller (Clojure team)20:11:20

in this particular case, maybe it doesn't matter, but I generally think minimizing the expansion and moving work to the macro is a good preference

Alex Miller (Clojure team)20:11:43

prob matters even more in cljs


because it's less confusing than mixing syntax-quote and unquote multiple times you mean?

Alex Miller (Clojure team)20:11:55

also that, although that's a separate point

Alex Miller (Clojure team)20:11:53

if you separate out all the data transformation / construction at the top of the macro, you can more easily test it (and put it in a function, which often ends up turning into a non-macro variant that is separately useful)

Alex Miller (Clojure team)20:11:22

I've seen that movie a few dozen times :)


true. the macroexpansion of my macro looks like this btw:

(do (def CANON_EQ java.util.regex.Pattern/CANON_EQ) (def CASE_INSENSITIVE java.util.regex.Pattern/CASE_INSENSITIVE))
(it doesn't show the metadata, but it should be there as well)

Alex Miller (Clojure team)20:11:11

yeah, that's what you want, not a bunch of for blather


this is what is generated by the for blather, I think that's where the confusion in this discussion comes from. the for is compile time.

Alex Miller (Clojure team)20:11:54

if you go aot this, do you want it to happen once at aot compile or on every load?

Alex Miller (Clojure team)20:11:48

I want to do as much as I can once at aot time


yes, me too. sooo?

Alex Miller (Clojure team)20:11:29

so you want the for in the macro, not in the expansion


yes, that's what's already the case, I've tried to communicate that several times now 🙂

Alex Miller (Clojure team)20:11:06

maybe we aree just in violent agreement then :)

Alex Miller (Clojure team)20:11:45

I thought above you were disagreeing but maybe I misinterpreted


no, I agree with what you say, but it seemed you were thinking the macro generated a runtime for

Alex Miller (Clojure team)20:11:34

nope, I think we were just talking past each other

Alex Miller (Clojure team)20:11:53

I was talking about the original code


yes, the original code also had that

Alex Miller (Clojure team)20:11:12

ah, right - again, I have it stuck in my head that the for was in the expansion and not in an unquote

Alex Miller (Clojure team)20:11:24

sorry, just my brain not working

Alex Miller (Clojure team)20:11:10

so yes, the other point is that minimizing the nested syntax quote / unquote makes it more readable


no problem 🙂 nested syntax-quote / unquote can be confusing, that's why it's probably easier to read with minimal back and forth between the two


nice conclusion

Alex Miller (Clojure team)20:11:47

sorry for the confusion!


@hiredman enlighten me with your non-macro


user=> (doc intern)
([ns name] [ns name val])
  Finds or creates a var named by the symbol name in the namespace
  ns (which can be a symbol or a namespace), setting its root binding
  to val if supplied. The namespace must exist. The var will adopt any
  metadata from the name symbol.  Returns the var.

Alex Miller (Clojure team)20:11:36

basically call the thing that def calls


ok, yeah, I've used intern before. I'll give it a go


@hiredman so what do I put on the dots here?

        :let [full-name (symbol (str "java.util.regex.Pattern/" constant-name))
              meta {:bb/export-constant true
                    :bb/full-name full-name}
              constant-name (with-meta constant-name meta)]]
  (intern *ns* constant-name ...))


I guess I need to use some reflection api for that. meh


also I'm not sure if this will work in CLJS


Oh, for sure not


(I tend to write code that works in .cljc)


But regexes in cljs are different anyway


They JavaScript regexes not Java Patterns


for the dots I would need some reflection API right?


The doseq becomes a join on the static fields of pattern, where the field name equals your constant name, then you take the fields that come out and call get to get the value


(defn every-single-valid-move [game-state player-id]
  (binding [*log-invalid-move-attempts* false]
      (fn [move]
        (not (f/failed? (make-move game-state
                                   (fn [] (first tile/all-tiles))))))
      (every-single-possible-move game-state player-id))))


getting some wierd behaviour with this binding


(def ^:dynamic *log-invalid-move-attempts* true)

(defmacro log-invalid-move-attempt [msg & rest]
  `(when *log-invalid-move-attempts*
     (log/error ~msg [email protected])))


no matter what i do, logging always happens


the lazy-seq escapes the binding block


either do the binding inside the fn, or eagerly evaluate before escaping that block


it might look nicer if telling it whether to log invalid move attempts was an arg to make-move instead of a dynamic var


pulls Scooby Doo mask off of clojure issue


Lazy sequences!


> the mix of lazy sequences with side effects

💯 4