Dynamic Require API - Callback Arguments

16 views
Skip to first unread message

Kris Kowal

unread,
Jan 26, 2011, 7:56:03 PM1/26/11
to comm...@googlegroups.com
On Wed, Jan 26, 2011 at 2:06 PM, khs4473 <khs...@gmail.com> wrote:
> - Whether the exports of loaded modules should be injected as callback
> arguments or made available through require:

A.) LOAD(..., function(a, b, c) { ... });
B.) LOAD(..., function() { require("a"); require("b"); require("c"); }
C.) LOAD(..., function(require, exports, module) { require("a"); }

Current show of hands. Please copy and alter in your responses.

A.)
B.) Christoph Dorn
C.) Christoph Dorn, Kris Kowal

Christoph Dorn on B or C: "This is most consistent with Modules/1.1
and represents the intended behavior where once LOAD() has satisfied
loading the modules they are available to the current sandbox just
like any other module."

Kris Kowal on C: This would permit the availability of modules to be
enforced on the inner block, given what working sets have been
explicitly required. This was one outcome of the discussion with Wes
Garland on require.ensure, and I think it fits here.

Kris Kowal

Mikeal Rogers

unread,
Jan 26, 2011, 7:59:01 PM1/26/11
to comm...@googlegroups.com
A.)
B.) Christoph Dorn, Mikeal Rogers

C.) Christoph Dorn, Kris Kowal

+1 for existing semantics.

johnjbarton

unread,
Jan 26, 2011, 8:18:04 PM1/26/11
to CommonJS


On Jan 26, 4:59 pm, Mikeal Rogers <mikeal.rog...@gmail.com> wrote:
> A.) John J Barton
> B.) Christoph Dorn, Mikeal Rogers, John J Barton
> C.) Christoph Dorn, Kris Kowal,

The callback argument solution works nicely for common cases where you
have a few dependencies and you want to bind them to variables simply.
This also means you can program without require("").

This form also means that the binding is checked up front. The
require("a") delays evaluation and the intervening code can take a
long time. Even in the case where you don't want that, it can be
valuable to have the option.

The zero argument form is ok and deals with the case where you have a
lot of dependencies. I don't understand why you'd want the three
argument form.

jjb

Kris Kowal

unread,
Jan 26, 2011, 8:32:16 PM1/26/11
to comm...@googlegroups.com
On Wed, Jan 26, 2011 at 5:18 PM, johnjbarton
<johnj...@johnjbarton.com> wrote:
> This form also means that the binding is checked up front. The
> require("a") delays evaluation and the intervening code can take a
> long time.

Lazy loading is accommodated by both options A and C. B does not
because it is not possible to statically distinguish the require calls
inside the callback from those outside, which is the point you appear
to address here. Note that C does not necessarily have this weakness.

> The zero argument form is ok and deals with the case where you have a
> lot of dependencies. I don't understand why you'd want the three
> argument form.

This form permits a different require function to be provided in the
inner and outer scope which would enforce the contents of the working
set in the inner and outer scopes. If the LOAD call augments an
existing working set of modules, it is possible for there to be an
unnoticed race condition if "require" is called outside the scope of
the LOAD callback for a module that was loaded by the LOAD call.
While it is not necessary to separate the working sets of the inner
and outer require calls in case C, it is at least possible to turn the
race condition into an early error, by having the outer require object
refuse to provide modules from the extended working set.

I am opposed to option B because it would preclude using this API for
lazy loading. I could be convinced to switch to A from C, but I've
written too many modules where C would be far more desirable than A
because of the quantity of bindings.

No changes:

James Burke

unread,
Jan 26, 2011, 8:32:44 PM1/26/11
to comm...@googlegroups.com
On Wed, Jan 26, 2011 at 2:56 PM, Kris Kowal <kris....@cixar.com> wrote:
> On Wed, Jan 26, 2011 at 2:06 PM, khs4473 <khs...@gmail.com> wrote:
>> - Whether the exports of loaded modules should be injected as callback
>> arguments or made available through require:
>
> A.) LOAD(..., function(a, b, c) { ... });
> B.) LOAD(..., function() { require("a"); require("b"); require("c"); }
> C.) LOAD(..., function(require, exports, module) { require("a"); }

A.) +1 James Burke

exports and module in particular do not make sense in that function
callback. The require from the containing scope can be used inside the
callback, so that is why I do not prefer C.). It is too much typing to
have to re-type the dependency name with require() as indicated in B).

James

johnjbarton

unread,
Jan 26, 2011, 8:42:23 PM1/26/11
to CommonJS


On Jan 26, 5:32 pm, Kris Kowal <kris.ko...@cixar.com> wrote:

> I am opposed to option B because it would preclude using this API for
> lazy loading. I could be convinced to switch to A from C, but I've
> written too many modules where C would be far more desirable than A
> because of the quantity of bindings.

I don't understand why B precludes lazy loading, but assuming it's
true I would unvote B.

I added Burke to A he failed the protocol ;-)

> A.) John J Barton, Jame Burke
> B.) Christoph Dorn, Mikeal Rogers

Christoph Dorn

unread,
Jan 26, 2011, 8:57:59 PM1/26/11
to comm...@googlegroups.com
On 11-01-26 5:32 PM, Kris Kowal wrote:
> On Wed, Jan 26, 2011 at 5:18 PM, johnjbarton
> <johnj...@johnjbarton.com> wrote:
>> This form also means that the binding is checked up front. The
>> require("a") delays evaluation and the intervening code can take a
>> long time.
>
> Lazy loading is accommodated by both options A and C. B does not
> because it is not possible to statically distinguish the require calls
> inside the callback from those outside, which is the point you appear
> to address here. Note that C does not necessarily have this weakness.

This is a critical point. The objective of LOAD() is to provide a
boundary between the current program and a set of optionally loaded modules.

Static analysis / require() scraping must stop at the LOAD() boundary.

It follows that the boundary must be detectable. I see two solutions:

1) Disallow require() within LOAD()
2) Make LOAD() predictably parsable

(1) applies sensibly only to option (A).
(2) could work with all three but unreliable. I thus suggest to drop
inner require() calls completely.

Updated options:

A.) LOAD(..., function(a, b, c) { ... });
B.) LOAD(..., function() { require("a"); require("b"); require("c"); }
C.) LOAD(..., function(require, exports, module) { require("a"); }

E.) LOAD(..., function(a, b, c) { ... }); - no internal require()

I change my vote to (E). Argument:

(E) is the most robust solution by not allowing require() calls within
LOAD() as it puts the responsibility on the developer vs static analysis
trickery. LOAD()ing extra code this way is designed to load *bootstrap*
modules into a sandbox where the bootstrap module should require()
dependencies vs a bunch of dependencies given to LOAD().

When writing CommonJS programs code belongs into one module per file.
Period. Providing any facility that seemingly makes magic module scopes
available (B, C) is misleading. I think we are better off declaring that
the factory/callback of LOAD() is simply a callback that triggers when
[deps] are met with module references provided if desired.

A.) John J Barton, Jame Burke

B.) Mikeal Rogers
C.) Kris Kowal
E.) Christoph Dorn

Christoph

Kris Kowal

unread,
Jan 26, 2011, 8:58:32 PM1/26/11
to comm...@googlegroups.com
On Wed, Jan 26, 2011 at 5:42 PM, johnjbarton
<johnj...@johnjbarton.com> wrote:
> I don't understand why B precludes lazy loading, but assuming it's
> true I would unvote B.

Assuming that the outer module is not wrapped, it is necessary to
scrape the content for require() calls, which are not distinguishable
between the inner and outer scopes. Assuming that the outer module is
wrapped, which you appear to assume, you are right that form B does
not preclude lazy loading. We should probably clarify that.

We also know that A and C are mutually exclusive. A+B, B+C are not.

A.) John J Barton, Jame Burke

B.1.) Christoph Dorn, Mikeal Rogers, John J Barton, assuming that
modules are necessarily wrapped with a "define" call
B.2.) Christoph Dorn, Mikeal Rogers, assuming that modules are not
necessarily wrapped with a "define" call


C.) Christoph Dorn, Kris Kowal

Kris Kowal

khs4473

unread,
Jan 26, 2011, 9:02:14 PM1/26/11
to CommonJS
A.

This may have been mentioned previously, but using differentiating
require within a LOAD and require outside of it will require a parsing
a context-free grammer - regular expressions will no longer suffice.

Furthermore, if you've stated your intention to load a module via
LOAD, then why must one restate that very same intention though
require? DRY.

khs

On Jan 26, 8:58 pm, Kris Kowal <kris.ko...@cixar.com> wrote:
> On Wed, Jan 26, 2011 at 5:42 PM, johnjbarton
>

Kris Kowal

unread,
Jan 26, 2011, 9:06:53 PM1/26/11
to comm...@googlegroups.com
You win one Kris. I was going to join the E camp, but I'm content that
require calls be limited to the working set of the outer require
inside the callback. This can be accomplished without mandating that
require not be used inside the load. Perhaps we could rephrase that E
means that require calls may only load modules in the working set of
the parent module, not including those loaded lazily. My decision is
also based on the assumption from "Unification of Require and
Provide", that these forms will only be used to require and not to
provide.

I think that enough has been clarified that others might reconsider their votes.

A.) LOAD(..., function (...modules) { require permitted })
B.1.) LOAD(..., function () { require permitted } necessarily wrapped
B.2.) LOAD(..., function () { require permitted } not necessarily wrapped
C.) LOAD(..., function (require, exports, module) { require permitted )
D.) ?
E.) LOAD(..., function (...modules) { require not permitted })

A.) John J Barton, Jame Burke, Kris Kowal
B.1.) Mikeal Rogers, John J Barton
B.2.) Mikeal Rogers
C.) No-one
D.) No-one
E.) Christoph Dorn

Christoph Dorn

unread,
Jan 26, 2011, 9:22:57 PM1/26/11
to comm...@googlegroups.com
E comes after C no? :)

Updating my vote based on Kris K's interpretation.

A.) John J Barton, Jame Burke, Kris Kowal, Christoph Dorn


B.1.) Mikeal Rogers, John J Barton
B.2.) Mikeal Rogers
C.) No-one
D.) No-one

E.) No-one

Christoph

Kris Kowal

unread,
Jan 26, 2011, 9:32:38 PM1/26/11
to comm...@googlegroups.com
On Wed, Jan 26, 2011 at 6:22 PM, Christoph Dorn
<christ...@christophdorn.com> wrote:
> E comes after C no? :)

In the comedy business, I think they call that a soft-ball. The
implication that E is "Profit" was not missed, which compelled ne to
vote for on that reason alone, but thought better of A.

Re-clarification for scanners:

A.) LOAD(..., function (...modules) { })
require is permitted inside the function block, but is only guaranteed
to be able to return the exports of the working set of the
containing module.
B.1.) LOAD(..., function () {}
where the containing module is necessarily wrapped with something
like a DECLARE block
(pending a decision on whether LOAD and DECLARE are equivalent)
B.2.) LOAD(..., function () {})
where the containing module is not necessarily wrapped with DECLARE, meaning
that require calls must be scraped for dependencies, such that require calls
both inside and outside the load block are effectively static dependencies
C.) LOAD(..., function (require, exports, module) {})
where the outer require is only guaranteed to be able to return the exports
of the working set of the containing module, and the inner require
is guaranteed
to be able to return the exports of the working sets of all
mentioned modules
and the parent module.

Hands:

A.) John J Barton, Jame Burke, Kris Kowal, Christoph Dorn
B.1.) Mikeal Rogers, John J Barton
B.2.) Mikeal Rogers
C.) No-one

I've redacted D and E since their champion has withdrawn.

Kris Kowal

johnjbarton

unread,
Jan 27, 2011, 12:09:17 AM1/27/11
to CommonJS


On Jan 26, 5:58 pm, Kris Kowal <kris.ko...@cixar.com> wrote:
> On Wed, Jan 26, 2011 at 5:42 PM, johnjbarton
>
> <johnjbar...@johnjbarton.com> wrote:
> > I don't understand why B precludes lazy loading, but assuming it's
> > true I would unvote B.
>
> Assuming that the outer module is not wrapped, it is necessary to
> scrape the content for require() calls, which are not distinguishable
> between the inner and outer scopes.  Assuming that the outer module is
> wrapped, which you appear to assume, you are right that form B does
> not preclude lazy loading.  We should probably clarify that.

Maybe there is a better solution: create a new function name that is
not "require()", say ns.resolve(). We don't scrape for them.

ns.resolve(id) looks up the id. If the module is not loaded, error.
Else if the module is not evaluated, evaluate. Return the module.

No conflict with Modules 1.1. You can call it in or out of the factory
function, but don't be surprised if it fails unless you have loaded
the dependency. It simply looks up the id and returns the module,
something I'd want in a loader.

This supports B choice. One of them anyway ;-).
jjb

Tom Robinson

unread,
Jan 27, 2011, 4:07:54 AM1/27/11
to comm...@googlegroups.com
A.

Question though: should this be guaranteed to work?

LOAD("a", function() {
var a = require("a");
});

since by the time the callback is executed "a" is guaranteed to be loaded?

Granted it's more verbose, but is there any reason it wouldn't technically work?

> A.) John J Barton, Jame Burke, Kris Kowal, Christoph Dorn, Tom Robinson


> B.1.) Mikeal Rogers, John J Barton
> B.2.) Mikeal Rogers
> C.) No-one
>
> I've redacted D and E since their champion has withdrawn.
>
> Kris Kowal
>

> --
> You received this message because you are subscribed to the Google Groups "CommonJS" group.
> To post to this group, send email to comm...@googlegroups.com.
> To unsubscribe from this group, send email to commonjs+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/commonjs?hl=en.
>

Kris Kowal

unread,
Jan 27, 2011, 4:20:10 AM1/27/11
to comm...@googlegroups.com
On Thu, Jan 27, 2011 at 1:07 AM, Tom Robinson <tlrob...@gmail.com> wrote:
> Question though: should this be guaranteed to work?
>
>    LOAD("a", function() {
>        var a = require("a");
>    });
>
> since by the time the callback is executed "a" is guaranteed to be loaded?
> Granted it's more verbose, but is there any reason it wouldn't technically work?

In this case, "a" becomes a static dependency of the containing
module, so the LOAD call becomes superfluous and "a" is not loaded
lazily. I had a thought that a loader might be written to prevent a
race-condition by denying the load of "a", but the mechanism is far
too convoluted to even bother working around. Let's drop the caveat to
A.

Kris Kowal

Irakli Gozalishvili

unread,
Jan 27, 2011, 9:34:37 AM1/27/11
to comm...@googlegroups.com
I would prefer D, where LOAD returns `exports` that are fulfilled async. Also moment of fulfill can be observed via some function.

var foo = LOAD('foo');
var bar = LOAD('bar);
var baz = LOAD('baz');

WHEN(foo, function() {
   foo.doit();
})

WHEN(bar, baz, function() {
   bar.method(baz)
})

Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/
Address: 29 Rue Saint-Georges, 75009 Paris, France


A.

Kris Kowal

Hannes Wallnoefer

unread,
Jan 27, 2011, 10:39:24 AM1/27/11
to comm...@googlegroups.com
I would have assumed that require() scraping and use of LOAD() could
not be combined in one script. I think the purpose of LOAD() is to get
rid of the need for static analysis, so you either use explicit or
automatic loading, but not both at the same time. IMO that would be a
simpler solution than the ones proposed above.

If a module loader still wants to implement automatic require()
scraping, it can simply look for instances of LOAD() before starting
to scan for require() calls, and abort scraping if there are any.

On that background, my vote is for B:

LOAD(..., function() { require("a"); require("b"); require("c"); }

In case require wasn't available already outside the callback, a B/C
hybrid could make sense (as James noted, there's no use for passing
exports/module in LOAD callback):

LOAD(..., function(require) { require("a"); require("b"); require("c"); }

Hannes

2011/1/27 Kris Kowal <kris....@cixar.com>:

Wes Garland

unread,
Jan 27, 2011, 11:07:54 AM1/27/11
to comm...@googlegroups.com
I'm having a hard time understanding this proposal.

Is this another case of "specify the dependencies of module X in whatever file *uses* module X?"

I have argued in the past the dependencies of X should be closely coupled with X - preferably annotated in the same file. I still hold this view.

Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

khs4473

unread,
Jan 27, 2011, 11:16:59 AM1/27/11
to CommonJS
The proposition is that require and LOAD serve two different
purposes: require is for specifying static dependencies. It is a
"psuedo"-keyword and suitable for static analysis (ie "scaping").
LOAD is for dynamic module loading. This is when you want to load a
module but you don't know what it will be until runtime.

They are complementary. Modules 1.1 conflates the two concerns and
this proposal is an attempt to address that.

khs


On Jan 27, 10:39 am, Hannes Wallnoefer <hann...@gmail.com> wrote:
> I would have assumed that require() scraping and use of LOAD() could
> not be combined in one script. I think the purpose of LOAD() is to get
> rid of the need for static analysis, so you either use explicit or
> automatic loading, but not both at the same time. IMO that would be a
> simpler solution than the ones proposed above.
>
> If a module loader still wants to implement automatic require()
> scraping, it can simply look for instances of LOAD() before starting
> to scan for require() calls, and abort scraping if there are any.
>
> On that background, my vote is for B:
>
> LOAD(..., function() { require("a"); require("b"); require("c"); }
>
> In case require wasn't available already outside the callback, a B/C
> hybrid could make sense (as James noted, there's no use for passing
> exports/module in LOAD callback):
>
> LOAD(..., function(require) { require("a"); require("b"); require("c"); }
>
> Hannes
>
> 2011/1/27 Kris Kowal <kris.ko...@cixar.com>:

khs4473

unread,
Jan 27, 2011, 11:19:00 AM1/27/11
to CommonJS
No - think of this LOAD as analogous to the Module Loaders API
proposal for Harmony. It's a way to load modules (not "dependencies")
at runtime, when you may not know the identity of the module you want
to load, until runtime.

khs

johnjbarton

unread,
Jan 27, 2011, 11:19:36 AM1/27/11
to CommonJS


On Jan 27, 1:07 am, Tom Robinson <tlrobin...@gmail.com> wrote:
> A.
>
> Question though: should this be guaranteed to work?
>
>     LOAD("a", function() {
>         var a = require("a");
>     });
>
> since by the time the callback is executed "a" is guaranteed to be loaded?

This should be an error, detected by the loader, implies infinite
recursion.

>
> Granted it's more verbose, but is there any reason it wouldn't technically work?

The reason this cannot work (assuming my model for how LOAD and
require work):
LOAD("a", f); enters name "a" a table and queues its load;
var a = require("a"); looks up "a" and returns its module,
evaluating if needed.
but we execute that statement only when we are already "evaluating
if needed".
So it must be a error.

jjb

johnjbarton

unread,
Jan 27, 2011, 11:25:16 AM1/27/11
to CommonJS


On Jan 27, 8:07 am, Wes Garland <w...@page.ca> wrote:
> I'm having a hard time understanding this proposal.
>
> Is this another case of "specify the dependencies of module X in whatever
> file *uses* module X?"
>
> I have argued in the past the dependencies of X should be closely coupled
> with X - preferably annotated in the same file. I still hold this view.

If you think about the examples as if they were written:
LOAD("main", [deps], callback);
then you will see that the dependencies of "main" are in the file that
uses "main". But of course it is forbidden to write the examples in
this way.

jjb

Wes Garland

unread,
Jan 27, 2011, 11:26:01 AM1/27/11
to comm...@googlegroups.com
On Thu, Jan 27, 2011 at 11:16 AM, khs4473 <khs...@gmail.com> wrote:
The proposition is that require and LOAD serve two different
purposes:  require is for specifying static dependencies.  It is a
"psuedo"-keyword and suitable for static analysis (ie "scaping").
LOAD is for dynamic module loading.  This is when you want to load a
module but you don't know what it will be until runtime.

so, LOAD is ~ require.async, module.provide, etc?

If that's the case, why do I see examples where a module declaration is written where a completion callback would go?

And why the hell are we writing keyworks in CAPS LOCK?!

Wes

khs4473

unread,
Jan 27, 2011, 11:52:34 AM1/27/11
to CommonJS
We're just using LOAD as a placeholder for module.load, require.load,
etc.


On Jan 27, 11:26 am, Wes Garland <w...@page.ca> wrote:

Tom Robinson

unread,
Jan 27, 2011, 12:28:16 PM1/27/11
to comm...@googlegroups.com
I agree this is getting confusing. I think we need a wiki page summarizing these APIs / votes.
--

Christoph Dorn

unread,
Jan 27, 2011, 12:46:02 PM1/27/11
to comm...@googlegroups.com

On 11-01-27 7:39 AM, Hannes Wallnoefer wrote:
> I would have assumed that require() scraping and use of LOAD() could
> not be combined in one script. I think the purpose of LOAD() is to get
> rid of the need for static analysis, so you either use explicit or
> automatic loading, but not both at the same time. IMO that would be a
> simpler solution than the ones proposed above.

LOAD() is designed to be combined with require scraping to *load*
additional sets of modules into the current program that should
expressly not be included in the initial program arrived at via require
scraping (or following require()s in a sync environment).

Christoph

Wes Garland

unread,
Jan 27, 2011, 1:30:32 PM1/27/11
to comm...@googlegroups.com
On Thu, Jan 27, 2011 at 11:19 AM, khs4473 <khs...@gmail.com> wrote:
No - think of this LOAD as analogous to the Module Loaders API
proposal for Harmony.  It's a way to load modules (not "dependencies")
at runtime, when you may not know the identity of the module you want
to load, until runtime.

Not dependencies of a current module -- but LOAD *must* load the dependencies of any module it loads.

Wes

Christoph Dorn

unread,
Jan 27, 2011, 1:36:48 PM1/27/11
to comm...@googlegroups.com
On 11-01-27 10:30 AM, Wes Garland wrote:
> On Thu, Jan 27, 2011 at 11:19 AM, khs4473 <khs...@gmail.com
> <mailto:khs...@gmail.com>> wrote:
>
> No - think of this LOAD as analogous to the Module Loaders API
> proposal for Harmony. It's a way to load modules (not "dependencies")
> at runtime, when you may not know the identity of the module you want
> to load, until runtime.
>
> Not dependencies of a current module -- but LOAD *must* load the
> dependencies of any module it loads.

Correct. It's a way to async load an extra module set into a sandbox
seeded by one or more module IDs passed to LOAD that are resolved in the
same fashion as IDs passed to require() within the same module.

Christoph

Kris Kowal

unread,
Jan 27, 2011, 1:47:49 PM1/27/11
to comm...@googlegroups.com
A.) LOAD(..., function (...modules) { })
B.1.) LOAD(..., function () {}) if the module is DECLARE()ed
B.2.) LOAD(..., function () {}) if the module is not DECLARE()ed

C.) LOAD(..., function (require, exports, module) {})
D.) LOAD(...) returns the incomplete exports object which somehow can
also be used as a promise

Hands:

A.) John J Barton, Jame Burke, Kris Kowal, Christoph Dorn, Tom Robinson
B.1.) Mikeal Rogers, John J Barton
B.2.) Mikeal Rogers
C.) No-one

D.) Irakli Gozalishvili

Reply all
Reply to author
Forward
0 new messages