ClojureScript

Support require outside of ns

Details

  • Type: Enhancement Enhancement
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: None
  • Fix Version/s: 1.9.293
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code and Test
  1. CLJS-1346.patch
    30/Sep/16 2:32 PM
    36 kB
    António Nuno Monteiro
  2. CLJS-1346-2.patch
    30/Sep/16 3:57 PM
    31 kB
    António Nuno Monteiro

Activity

Hide
António Nuno Monteiro added a comment -

My initial thoughts to tackle this one would be:

  • add support for the `require` special form (along with `require-macros`, `use`, `use-macros`, `refer-clojure` and `import` for completeness) in the analyzer.
  • have `cljs.analyzer/parse-ns` recognize more than the `:ns` AST :op, merging `require`s/`uses` etc into NS requires. Use the 'cljs.user NS by default if no NS form found.
  • this means that `cljs.closure/find-cljs-dependencies` can get at the `require`d dependencies in the build pipeline.

Pending questions:

  • How to disallow `require` etc. except at the top level?
  • Possibly only even allow it strictly after the NS form, or one or the other?
Show
António Nuno Monteiro added a comment - My initial thoughts to tackle this one would be:
  • add support for the `require` special form (along with `require-macros`, `use`, `use-macros`, `refer-clojure` and `import` for completeness) in the analyzer.
  • have `cljs.analyzer/parse-ns` recognize more than the `:ns` AST :op, merging `require`s/`uses` etc into NS requires. Use the 'cljs.user NS by default if no NS form found.
  • this means that `cljs.closure/find-cljs-dependencies` can get at the `require`d dependencies in the build pipeline.
Pending questions:
  • How to disallow `require` etc. except at the top level?
  • Possibly only even allow it strictly after the NS form, or one or the other?
Hide
David Nolen added a comment -

I think we could easily reify the notion of the top level in the analyzer, i.e. `:top true` in the env if we're at the top. To me a harder (hardest?) question is how to allow separately compiled files with no namespaces to see each other's definitions as one would expect from the user namespace?

Show
David Nolen added a comment - I think we could easily reify the notion of the top level in the analyzer, i.e. `:top true` in the env if we're at the top. To me a harder (hardest?) question is how to allow separately compiled files with no namespaces to see each other's definitions as one would expect from the user namespace?
Hide
António Nuno Monteiro added a comment -

As per the Slack discussion, solving this issue could probably be done by:

  • having a `:toplevel` key in the analyzer environment that is true only if we're at the top
  • add new special forms to the analyzer (`require`, etc.)
  • for files that don't have `ns` as its first form, generate a `cljs.user` (random) sub-namespace
  • create a single cljs.user file with several `goog.provide` statements of its sub-namespaces
  • the above needs a custom var resolver in order to actually work and be useful
Show
António Nuno Monteiro added a comment - As per the Slack discussion, solving this issue could probably be done by:
  • having a `:toplevel` key in the analyzer environment that is true only if we're at the top
  • add new special forms to the analyzer (`require`, etc.)
  • for files that don't have `ns` as its first form, generate a `cljs.user` (random) sub-namespace
  • create a single cljs.user file with several `goog.provide` statements of its sub-namespaces
  • the above needs a custom var resolver in order to actually work and be useful
Hide
Thomas Heller added a comment -

My question would be: What does this solve that ns doesn't already solve in a clearer/saner way?

My concern is that require can never work the way it does in CLJ due to the async nature of JS engines (+ what closure expects), but if we introduce something that has a counterpart in CLJ people will expect to use it this way and be confused if it doesn't (ie. conditional require, run code and expect the next require to see it).

I understand that the motivation for this is "for scripts" but wouldn't we be better off with a simple tooling change that can compile "scripts" by asking for a fn (ie. my.script/useful-fn) with the usual rules? The compiler inventing random ns names with a custom var resolver sounds a whole lot complicated for little to no gain?

Show
Thomas Heller added a comment - My question would be: What does this solve that ns doesn't already solve in a clearer/saner way? My concern is that require can never work the way it does in CLJ due to the async nature of JS engines (+ what closure expects), but if we introduce something that has a counterpart in CLJ people will expect to use it this way and be confused if it doesn't (ie. conditional require, run code and expect the next require to see it). I understand that the motivation for this is "for scripts" but wouldn't we be better off with a simple tooling change that can compile "scripts" by asking for a fn (ie. my.script/useful-fn) with the usual rules? The compiler inventing random ns names with a custom var resolver sounds a whole lot complicated for little to no gain?
Hide
António Nuno Monteiro added a comment -

I understand your concerns, but I think that the `:toplevel` key in env would let us produce useful error messages that would guide users in the right direction.

Show
António Nuno Monteiro added a comment - I understand your concerns, but I think that the `:toplevel` key in env would let us produce useful error messages that would guide users in the right direction.
Hide
David Nolen added a comment - - edited

This isn't just for scripts. There are other typical patterns that needs this like data literal files. Having a significant difference from Clojure here when we can in fact largely close the gap is and has always been undesirable. The outlined plan does not introduce any significant complexity - with cljs.user subnamespaces (these should have stable names not random ones btw) + custom resolve for cljs.user vars we will produce code that is perfectly usable for both runtime dev and advanced compilation.

Show
David Nolen added a comment - - edited This isn't just for scripts. There are other typical patterns that needs this like data literal files. Having a significant difference from Clojure here when we can in fact largely close the gap is and has always been undesirable. The outlined plan does not introduce any significant complexity - with cljs.user subnamespaces (these should have stable names not random ones btw) + custom resolve for cljs.user vars we will produce code that is perfectly usable for both runtime dev and advanced compilation.
Hide
David Nolen added a comment -

Turns out we don't need custom resolution. Google Closure doesn't care if we add definitions to some other namespace.

Show
David Nolen added a comment - Turns out we don't need custom resolution. Google Closure doesn't care if we add definitions to some other namespace.
Hide
António Nuno Monteiro added a comment -

Attached patch with proposed implementation and tests.

Show
António Nuno Monteiro added a comment - Attached patch with proposed implementation and tests.
Hide
António Nuno Monteiro added a comment -

Attached patch with `disallowing-ns*` instead of a `:top-level` in the analyzer environment.

Show
António Nuno Monteiro added a comment - Attached patch with `disallowing-ns*` instead of a `:top-level` in the analyzer environment.

People

Vote (1)
Watch (5)

Dates

  • Created:
    Updated:
    Resolved: