Harmony module execution (was Re: Split off AMD? (was Re: [CommonJS] New amd-implement list))

29 views
Skip to first unread message

James Burke

unread,
May 19, 2011, 5:20:38 PM5/19/11
to comm...@googlegroups.com
On Wed, May 18, 2011 at 6:59 AM, Kevin Dangoor <k...@blazingthings.com> wrote:
> I asked Dave Herman (author of the Harmony Modules spec) about this at
> JSConf and I'm thought the answer was that Harmony modules are lazily
> evaluated (assuming I'm not getting mixed up by the "lazy" terminology
> here). In other words if you have a module like this:
>
> module q = import Q
>
>
> the "Q" module is loaded before this module is run, but its code is not
> actually run until it hits the import statement here.

That is my understanding too, but all import and module declarations
need to be at the top level of a module, not as part of if/else, and
require only takes string literals. So it results in dependencies
executing before the main body of the current module executes. I think
it is more subtle than that, but here is what is not allowed:

var name;
if (someCondition) {
name = 'a1';
} else {
name = 'a2';
}

module m = require(name);

or this:

module m;
if (someCondition) {
m = require('a1');
}else{
m = require('a2');
}

The above cases should be handled through the Module Loader's
Loader.load(moduleName, function(moduleRef){}); API, which looks like
an early evaluation model (the function callback to Loader.load does
not then need to do a require() for the module to execute it).

This also seems like it would not be allowed, because 'module' parents
can only be ProgramElement(load), ExportDeclaration(load) or
ModuleElement(load),
and those items can only have ProgramElement(load) or Program(load) as
parents:

try{
module m = require('first');
}catch(e){
module m = require('backup');
}

What is less clear to me, and I am trying to get more clarification,
is if something like this is allowed:

//baz.js
module foo = require('foo.js');

//foo.js:
export var name = 'foo';
module bar = require('bar.js');
var barName = bar.name;

//bar.js:
export var name = 'bar';
module foo = require('foo.js');
//This works because foo.js did the export before the require?
var fooName = foo.name;

I have asked about this on the es-discuss list:
https://mail.mozilla.org/pipermail/es-discuss/2011-May/014507.html

End result though is that harmony modules prevent dynamic/computed
require cases that are allowed by CommonJS Modules 1.1. These result
in the CommonJS use of lazy evaluation, and are anti-patterns/break
when used in an async loading context like the browser.

The lazy evaluation in CommonJS Modules 1.1 may make some forms of
circular dependencies easier, but that very small benefit is
outweighed by encouraging coding practices that do not work in a
browser context.

Note that early/late evaluation difference only affects a small
percentage of modules. The vast majority are fine in either approach.

James

Kevin Dangoor

unread,
May 19, 2011, 9:28:53 PM5/19/11
to comm...@googlegroups.com
On Thu, May 19, 2011 at 5:20 PM, James Burke <jrb...@gmail.com> wrote:

End result though is that harmony modules prevent dynamic/computed
require cases that are allowed by CommonJS Modules 1.1. These result
in the CommonJS use of lazy evaluation, and are anti-patterns/break
when used in an async loading context like the browser.

I believe that the Modules spec says that what's in require() has to be a string:
http://wiki.commonjs.org/wiki/Modules/1.1

the browser loader that I wrote once upon a time scraped the source for require()s.

I believe you can still dynamically load Harmony modules by using the module loader API.
 

Note that early/late evaluation difference only affects a small
percentage of modules. The vast majority are fine in either approach.


Exactly. That's the most annoying thing about the whole debate.

Kevin


--
Kevin Dangoor

work: http://mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com

James Burke

unread,
May 19, 2011, 9:46:57 PM5/19/11
to comm...@googlegroups.com
On Thu, May 19, 2011 at 6:28 PM, Kevin Dangoor <k...@blazingthings.com> wrote:
> On Thu, May 19, 2011 at 5:20 PM, James Burke <jrb...@gmail.com> wrote:
>>
>> End result though is that harmony modules prevent dynamic/computed
>> require cases that are allowed by CommonJS Modules 1.1. These result
>> in the CommonJS use of lazy evaluation, and are anti-patterns/break
>> when used in an async loading context like the browser.
>
> I believe that the Modules spec says that what's in require() has to be a
> string:
> http://wiki.commonjs.org/wiki/Modules/1.1

That is not what has shown up in practice, at least in Node module
usage. The root of my examples were things I saw when trying to
convert Node modules.

> the browser loader that I wrote once upon a time scraped the source for
> require()s.

By having a traditional CommonJS modules loader parse the source
before evaluating it and *only* allow loading of modules that were
specified by a string literal, that would be something, it would
shrink the gap between early and lazy eval to almost zero. Well that,
and if it mandated require() calls to be top-level calls in a module,
not part of other statements. The only thing left would be some types
of circular dependencies that are brittle anyway.

However, by introducing that more stringent CommonJS module loader,
you will break existing code. I don't think for instance, node.js,
would do such a change.

> I believe you can still dynamically load Harmony modules by using the module
> loader API.

Correct, but that Harmony API is a callback API. The CommonJS modules
that have a problem are the ones that do a computed require but expect
sync require access. At least for the cases I have seen in Node. The
try{ var a = require('a'); } catch(e) { var a = require('a1') } are
particularly troublesome to see.

James

Reply all
Reply to author
Forward
0 new messages