Hey Jeff, I appreciate the comments. The fact that the Clojure REPL sets up 
a dynamic context (not just with *ns*, but with many other 
<https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L71-L88>
 
dynamic bindings as well) that we take  for granted is indeed confusing in 
the gen-class and other scenarios. If you want to file an issue for the 
reference docs on the site, the place to do that is here:

https://github.com/clojure/clojure-site/issues

Alex

On Thursday, June 22, 2017 at 9:24:24 PM UTC-5, Jeff Rabinowitz wrote:
>
> If I may be so bold to interject -- 
>
> I'm a colleague of Didier's, and I originally brought this to his 
> attention when I hit this issue at work. I just want to add that part of 
> the confusion arises from some miscommunication/incomplete information on 
> the official docs for Namespaces 
> <https://clojure.org/reference/namespaces>;
>
>
> At the Repl it’s best to use in-ns 
>> <https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/in-ns>,
>>  
>> in which case the new namespace will contain mappings only for the 
>> classnames in java.lang. In order to access the names from the 
>> clojure.core namespace you must execute (clojure.core/refer 
>> 'clojure.core). The user namespace at the Repl has already done this.
>
>  
>
> The current namespace, **ns** can and should be set only with a call to 
>> in-ns 
>> <https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/in-ns> 
>> or 
>> the ns macro 
>> <https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/ns>, 
>> both of which create the namespace if it doesn’t exist.
>
>
> I believe this is a major source of the confusion (at least it was for 
> me). The official documentation *does* mention that in-ns is recommended 
> in the REPL; and in a subsequent paragraph, the official documentation 
> *does* state that in-ns (or ns but it macroexpands to in-ns as well so is 
> just as bad in context) is the preferred way of creating a namespace. But 
> nowhere on the official documentation page does it qualify that *ns*, as 
> a dynamic Var, will require curation if bootstrapping an application from 
> a static context! Instead, users are thrown with the ugly 
> IllegalStateException("Can't 
> change/establish root binding of: *ns* with set"), which is fairly 
> inscrutable in context. (Nowhere else that I'm aware of in the runtime can 
> a user not have ever created a dynamic var themselves, have manipulated it 
> without realizing they're using the set! API, and end up trying to set! 
> it at runtime before calling binding.) It's not a lie or intentionally 
> misleading, but allows an application developer to walk away with an 
> incomplete and inconsistent understanding of the operation of those 
> functions. Somehow the logical decision was taken at every step, but yet 
> there's room for users to trip up here.
>
> People who tinker with macros and evals are probably accustomed to 
> battling issues with dynamic vars, or at the very least are remembering 
> that dynamic vars have different semantics, and so can easily search online 
> to discover that they ran afoul of the Var stack frame issue and need to 
> add a binding. But in-ns is a real head-scratcher! If implemented in 
> Clojure, it would look like (set! clojure.core/*ns* (create-ns 
> 'some-namespace)), but because it's defined in RT.java, there's no source 
> available; what's more, the documentation attached to the Var for in-ns 
> doesn't allude to the mechanics it employs. 
>
> Hands down I can guarantee all Clojure developers who aren't involved in 
> the language maintenance will respond to the exception above by thinking, 
> "Let me check the source using the source macro and see what just 
> happened." They'll call clojure.repl/source and get -- nothing. Then 
> they'll check the docs which say -- "Sets *ns* to the namespace named by 
> the symbol, creating it if needed." While *technically* correct, this is 
> hands down more likely to be read in the colloquial English meaning of 
> "casually changes", rather than the Clojure-technical meaning of "Does 
> something akin to calling `set!`". And they'll either give up after an hour 
> of fruitless searching, or if they have *a lot of free time*, will spend 
> several hours poring over compiler source code 
> <http://justabloginthepark.com/2017/06/18/clojure-and-the-esoteric-mysteries-of-namespaces/>
>  
> until they realize that nobody documented this case; and that the mechanics 
> at runtime, while internally consistent, are completely counter-intuitive 
> given the information available on the public documentation.
>
> I'm glad that you're considering "plugging" the other Clojure entry points 
> to eliminate this stumbling block altogether. In the meantime, perhaps some 
> clarification of the documentation (either those attached to the Var 
> itself, or on the site, or both) are in order?
>
> Thanks,
>
> - Jeff
>
> On Thursday, June 22, 2017 at 3:47:17 PM UTC-4, Didier wrote:
>>
>> I opened a Jira enhancement: https://dev.clojure.org/jira/browse/CLJ-2185
>>
>> Hope I did a good job at it.
>>
>> On Wednesday, 21 June 2017 18:58:26 UTC-7, Alex Miller wrote:
>>>
>>> Was just musing some more - the Clojure Java API was added pretty late 
>>> (~1.6 or 1.7). I wonder if
>>>
>>> 1) changing the Java Clojure API to ensure a certain binding context and
>>> 2) changing the main() method to invoke the Clojure function through the 
>>> Java Clojure API
>>>
>>> would be a good end game. Would need a lot more analysis.
>>>
>>> On Wednesday, June 21, 2017 at 8:52:48 PM UTC-5, Alex Miller wrote:
>>>>
>>>>
>>>>
>>>> On Wednesday, June 21, 2017 at 6:09:43 PM UTC-5, Didier wrote:
>>>>>
>>>>> Great breakdown.
>>>>>
>>>>> 1. "compile and bootstrap yourself through gen-class" means: you 
>>>>>> generate a class and then invoke the main() method of that class
>>>>>>
>>>>>
>>>>> I'd go further then just calling main. So I mean you generate a class, 
>>>>> and every function call from Java using the generated class should be 
>>>>> executed as if it were executed through clojure.main, so with the 
>>>>> bindings 
>>>>> set and inside the user namespace.
>>>>>
>>>>
>>>> I do not think you should treat clojure.main as the gold standard. 
>>>> clojure.main is a helper program and it makes some choices that may or may 
>>>> not be what every program wants. There is nothing special about the "user" 
>>>> namespace for example. Other repls choose other things (pretty sure the 
>>>> boot repl uses boot.user example). Likewise, there's nothing special about 
>>>> the set of things clojure.main chooses to bind on startup - lein, boot, 
>>>> and 
>>>> other tools make different choices. If anything, I'd say #2 is what you 
>>>> should consider the most canonical case.
>>>>  
>>>>
>>>>>  
>>>>>
>>>>>> 2. "loaded at runtime" means: you (presumably) start the REPL, load a 
>>>>>> namespace (either from a source file or a class file), then invoke the 
>>>>>> -main function
>>>>>>
>>>>>
>>>>> Correct. It's the case of a non AOT compiled namespace being loaded 
>>>>> from one of the many (load) functions of Clojure. Such as:
>>>>>
>>>>> (load "dda/main")
>>>>> (dda.main/-main)
>>>>>  
>>>>>
>>>>>> 3. "bootstrapped through clojure.main" means: you invoke clojure.main 
>>>>>> (a compiled program provided with Clojure) with the -m arg specifying a 
>>>>>> namespace. This will start the Clojure runtime, load the namespace, then 
>>>>>> invoke the -main function
>>>>>>
>>>>>
>>>>> Correct.
>>>>>  
>>>>>
>>>>>> 4. "bootstrapped through lein" means: I assume this is "lein run" 
>>>>>> with a :main set or "lein run -m" but I think lein supports specifying a 
>>>>>> namespace or a function in a namespace or a class with a main() method. 
>>>>>> Depending which of those you're doing, this is like similar to either #1 
>>>>>> or 
>>>>>> #2 and here lein is effectively doing similar work as #3.
>>>>>>
>>>>>
>>>>> Correct, yes internally lein delegates to clojure.main I believe.
>>>>>
>>>>
>>>> No, I don't think lein uses clojure.main at all.
>>>>  
>>>>
>>>>>  
>>>>>
>>>>>> 5. There is a Clojure Java API (
>>>>>> http://clojure.github.io/clojure/javadoc/), but I'm not sure if you 
>>>>>> are actually referring to this or something else. Doing so would 
>>>>>> basically 
>>>>>> mean going through that API to do the same thing as #2. 
>>>>>>
>>>>>
>>>>> I am talking about that API. I believe it uses RT under the hood, and 
>>>>> ends up doing the same thing that #1 does. So in my test, if you did:
>>>>>
>>>>
>>>> This is actually the same as #2, not #1. It's not making an interop 
>>>> call, it's invoking a var function, just like any other Clojure call. 
>>>>
>>>>>
>>>>> IFn require = Clojure.var("dda.main", "-main");
>>>>> require.invoke();
>>>>>
>>>>> This will run inside "clojure.core", and will not have any of the 
>>>>> default bindings set available.
>>>>>
>>>>> I think if I were to restate your suggestion, I would say that a 
>>>>>> genclass-compiled main entry point should initialize the Clojure runtime 
>>>>>> and invoke the code in a binding as if it were being invoked from other 
>>>>>> Clojure code. I can see some logic in that (although that then also 
>>>>>> affects 
>>>>>> #3 and #4 as they go through this code too).
>>>>>>
>>>>>
>>>>> Ya, arguably, I wonder why the clojure runtime doesn't initialize the 
>>>>> bindings and sets the namespace to 'user before delegating execution back 
>>>>> to user code.
>>>>>
>>>>
>>>> Again, there is nothing special about user and I wouldn't get hung up 
>>>> on that. It does seem like it would be reasonable to set up a binding 
>>>> context for *ns* though so it's set!-able - that's the thing that's 
>>>> missing.
>>>>  
>>>>
>>>>> If it did, then every invocation of Clojure from Java would always be 
>>>>> bootstrapped in a similar way, and run under an equal context. 
>>>>> Clojure.main 
>>>>> wouldn't need to do it anymore.
>>>>>
>>>>> But there might be a good reason for not doing that inside the clojure 
>>>>> runtime. In which case, it still leaves open the question of should 
>>>>> clojure 
>>>>> invocations through a generated class, and invocations from the Clojure 
>>>>> API 
>>>>> mimic the bootstrapping of clojure.main?
>>>>>
>>>>
>>>> I would say, no. :) But it might be reasonable to mimic a Clojure 
>>>> function invocation that has a binding for *ns*. If you want to file a 
>>>> ticket, I'm happy to think about it more.
>>>>  
>>>>
>>>>>
>>>>> P.S.: I also know that in practice, this has not caused that many 
>>>>> issues, due to the fact that Clojure is now many years old, and I've 
>>>>> never 
>>>>> heard of other people complain about the discrepancies between generated 
>>>>> classes and the clojure API versus clojure.main. So I don't think its a 
>>>>> pressing issue, but I still feel it could be worth a thought.
>>>>>
>>>>>>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to