Clojure

require namespace implicitly

Details

  • Type: Enhancement Enhancement
  • Status: Closed Closed
  • Priority: Blocker Blocker
  • Resolution: Declined
  • Affects Version/s: None
  • Fix Version/s: Backlog
  • Component/s: None
  • Labels:
    None

Description

Referencing a function with a fully-qualified namespace should work without first using require or use, similar to how a fully-qualified java class can be used without importing it.

It's a small change in Compiler that tries to call (require x) if the fully qualified classname is not found. This should give priority to the java class, which protects backwards compatibility. There is no runtime performance impact, only compile time (the first time the namespace is seen). The fact that code (the namespace) is loaded during compilation of a form is no different than loading code to look up a java class.

This makes it easier to write quick scripts as in the example below, also to use one-liners in the repl or ad hoc in code.

For example: java -cp src/clj/:classes clojure.main -e "(clojure.set/union #{1} #{2})"

Currently on master, this produces: Exception in thread "main" java.lang.ClassNotFoundException: clojure.set
but this works: java -cp src/clj/:classes clojure.main -e "(require 'clojure.set) (clojure.set/union #{1} #{2})"

Obviously, (use) would make the code shorter, but my goal is to make it implicit.

Discussion: http://groups.google.com/group/clojure-dev/t/69823ce63dd94a0c

Activity

Hide
Assembla Importer added a comment -

mikehinchey said: [file:cQSfQ22L8r37zxeJe5cbCb]: patch to fix #461

Show
Assembla Importer added a comment - mikehinchey said: [file:cQSfQ22L8r37zxeJe5cbCb]: patch to fix #461
Hide
Assembla Importer added a comment -

mikehinchey said: The discussion shows some people want this, other's aren't sure. Attached the patch so people can try it out.

Show
Assembla Importer added a comment - mikehinchey said: The discussion shows some people want this, other's aren't sure. Attached the patch so people can try it out.
Hide
Stuart Sierra added a comment -

One problem I see: With this change, it becomes harder for code-reading tools to determine all the dependencies of a namespace without evaluating it. Right now, I can parse the "ns" declaration of any file and know its dependencies. (Obviously, this breaks if the file loads code outside of the "ns" declaration, but then static analysis is virtually impossible.)

With this change, the "ns" declaration no longer represents the complete set of dependencies for the namespace. I can try to read the whole file, but I have no way of knowing if "foo.bar.baz/quux" represents a namespace-qualified symbol or a static Java member, unless I evaluate it.

I think loading Java classes and loading Clojure namespaces are fundamentally different operations because classes, unlike namespaces, cannot change after they are loaded.

Show
Stuart Sierra added a comment - One problem I see: With this change, it becomes harder for code-reading tools to determine all the dependencies of a namespace without evaluating it. Right now, I can parse the "ns" declaration of any file and know its dependencies. (Obviously, this breaks if the file loads code outside of the "ns" declaration, but then static analysis is virtually impossible.) With this change, the "ns" declaration no longer represents the complete set of dependencies for the namespace. I can try to read the whole file, but I have no way of knowing if "foo.bar.baz/quux" represents a namespace-qualified symbol or a static Java member, unless I evaluate it. I think loading Java classes and loading Clojure namespaces are fundamentally different operations because classes, unlike namespaces, cannot change after they are loaded.
Hide
Paul Stadig added a comment -

Stuart,
On the first point: it is already hard for code-reading tools. One can (require 'something) outside of the ns form. There are also uses of eval, direct references to fully qualified classes, and other nefarious ways. You seem to have admitted this already, so I'm not quite sure what you are objecting to? Objection 1: OVERRULED.

On the second point: you already admitted above that the ns declaration doesn't represent the complete set of dependencies, so there is no "no longer" about it. It was just never the case. Secondly, "foo.bar.baz/quux" could be a static Java member, or a Clojure Var, but that is irrelevant to this patch. That was always the case, and the patch is about autoloading, not about interpreting to what "foo.bar.baz/quux" is referring. Objection 2: OVERRULED.

On the third point: again, I don't see the relevance of the fact that a namespace can be changed after it has been loaded but a class cannot. Again, the patch is about autoloading, and the immutability/mutability of namespaces vs. classes is orthogonal. Objection 3: OVERRULED.

Finally, the original ML thread that spawned this had a +1 from the following persons: myself, Christophe Grand, Phil Hagelberg, Laurent Petit, Steve Gilardi, Cosmin Stejerean, and Chas Emerick.

It had a -1 only from: you, Dimitry Gashinsky.

A negative comment from Stu Halloway, and a positive-ish comment from Chris Houser.

I say we move forward with this.

Show
Paul Stadig added a comment - Stuart, On the first point: it is already hard for code-reading tools. One can (require 'something) outside of the ns form. There are also uses of eval, direct references to fully qualified classes, and other nefarious ways. You seem to have admitted this already, so I'm not quite sure what you are objecting to? Objection 1: OVERRULED. On the second point: you already admitted above that the ns declaration doesn't represent the complete set of dependencies, so there is no "no longer" about it. It was just never the case. Secondly, "foo.bar.baz/quux" could be a static Java member, or a Clojure Var, but that is irrelevant to this patch. That was always the case, and the patch is about autoloading, not about interpreting to what "foo.bar.baz/quux" is referring. Objection 2: OVERRULED. On the third point: again, I don't see the relevance of the fact that a namespace can be changed after it has been loaded but a class cannot. Again, the patch is about autoloading, and the immutability/mutability of namespaces vs. classes is orthogonal. Objection 3: OVERRULED. Finally, the original ML thread that spawned this had a +1 from the following persons: myself, Christophe Grand, Phil Hagelberg, Laurent Petit, Steve Gilardi, Cosmin Stejerean, and Chas Emerick. It had a -1 only from: you, Dimitry Gashinsky. A negative comment from Stu Halloway, and a positive-ish comment from Chris Houser. I say we move forward with this.
Hide
Kevin Downey added a comment -

I have serious reservations about the complexity this will add to the compiler. the current patch is no good, it will break for aot compilation.

Show
Kevin Downey added a comment - I have serious reservations about the complexity this will add to the compiler. the current patch is no good, it will break for aot compilation.
Hide
Paul Stadig added a comment -

So I've been told that my tongue-in-cheek may not have translated well, but that was the intent. I apologize if that was the case.

My point is just to draw attention to this ticket again. It was discussed on the ML with several +1's and has been mentioned again in chat. I don't think any of the objections that Stuart Sierra raised are particularly relevant to the question of autoloading the namespace of a fully qualified var.

Has anyone tried the patch? Kevin Downey seems to think it will not work in the context of AOT.

Do we need a new patch?

Show
Paul Stadig added a comment - So I've been told that my tongue-in-cheek may not have translated well, but that was the intent. I apologize if that was the case. My point is just to draw attention to this ticket again. It was discussed on the ML with several +1's and has been mentioned again in chat. I don't think any of the objections that Stuart Sierra raised are particularly relevant to the question of autoloading the namespace of a fully qualified var. Has anyone tried the patch? Kevin Downey seems to think it will not work in the context of AOT. Do we need a new patch?
Hide
Kevin Downey added a comment -

the patch doesn't actually cause code to load the required namespaces to be generated. it only loads the required namespaces during compilation, which is why it breaks aot. once you get into code generation for aot it gets complicated, where does the generated code go? do we want to try and emit it separately like the requires from an ns form or does it get emitted in the middle of the particular function being compiled. I think the first approach is desirable from a stand point of correctness, but carries with it a load of complexity.

Show
Kevin Downey added a comment - the patch doesn't actually cause code to load the required namespaces to be generated. it only loads the required namespaces during compilation, which is why it breaks aot. once you get into code generation for aot it gets complicated, where does the generated code go? do we want to try and emit it separately like the requires from an ns form or does it get emitted in the middle of the particular function being compiled. I think the first approach is desirable from a stand point of correctness, but carries with it a load of complexity.
Hide
Paul Stadig added a comment -

I think it gets emitted in the middle of a function, just like would happen now if you do (require 'clojure.set) (clojure.set/union ...)

Is there a benefit to having it emit separately like an ns form? Isn't the ns form just a macro that turns into calls to (require ...) which happen to be at the top of a file because that's where the ns form is?

Show
Paul Stadig added a comment - I think it gets emitted in the middle of a function, just like would happen now if you do (require 'clojure.set) (clojure.set/union ...) Is there a benefit to having it emit separately like an ns form? Isn't the ns form just a macro that turns into calls to (require ...) which happen to be at the top of a file because that's where the ns form is?
Hide
Rich Hickey added a comment -

This is not a good idea, for many reasons, the simplest of which is: it makes loading a side effect of calling a function in a module. Since loading can have arbitrary effects, it shouldn't be implicit. This isn't warranted by the meager benefits it might provide.

Show
Rich Hickey added a comment - This is not a good idea, for many reasons, the simplest of which is: it makes loading a side effect of calling a function in a module. Since loading can have arbitrary effects, it shouldn't be implicit. This isn't warranted by the meager benefits it might provide.
Rich Hickey made changes -
Field Original Value New Value
Reporter Mike Hinchey [ mikehinchey ]
Priority Blocker [ 1 ]
Approval Test None
Rich Hickey made changes -
Status In Progress [ 3 ] Closed [ 6 ]
Resolution Declined [ 2 ]

People

Vote (1)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: