AMD vs Wrappings

70 views
Skip to first unread message

James Burke

unread,
Dec 28, 2010, 5:05:02 PM12/28/10
to comm...@googlegroups.com
I wrote up a blog post relating to AMD and the more recent Wrappings
proposal here:
http://tagneto.blogspot.com/2010/12/standards-and-proposals-for-javascript.html

My main concern with Wrappings is that the availability semantics do
not translate well to the future with Simple Modules.

James

Wes Garland

unread,
Dec 28, 2010, 7:58:27 PM12/28/10
to comm...@googlegroups.com
James;

I skimmed your blog and what I took away was that you are concerned about is that CommonJS wrapped modules have a different initialization semantics than the simple modules proposal, and as such you believe that module factory functions should be invoked during the dependency-resolution phase, rather upon the first use of the require() call.

There is only one place where this difference leaks out, and that is during the execution of code in the main body of the module. Availability of exports is a non-issue

This was discussed at length a couple of years ago on this list. In the era of the "pythonic modules" thread.  Do you remember why the standard was written the way it was? Have any of those reasons changed?

Are you also certain that simple modules require eager evaluation of the module declaration?

Also, aside from semantic compatibility with the simple modules proposal, are there any benefits to eager evaluation of module factory functions? There are definitely disadvantages, such as
  • breaks semantic compatibility with existing CommonJS modules
  • cpu cycles and memory consumed for modules which may never be needed
  • prevents server-side implementations from optimizing away unnecessary I/O from unused modules
I'm not sure the pros outweight the cons here -- any reasonable Simple Modules=>CommonJS machine translation idea I have considered can easily invoke require() on all dependent modules during initial program load, emulating SM module semantics.

Wes

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

khs4473

unread,
Dec 28, 2010, 10:32:26 PM12/28/10
to CommonJS
How CommonJS modules relate to Simple Modules is still iffy, as far as
I can tell. In Simple Modules, dependencies are statically declared.
When we use require with only string literals, we are effectively
emulating a static dependency declaration; we are "faking" a language-
level construct. In fact, there exists a one-to-one mapping between
require calls (with string literals) and external module declarations
in Simple Modules:

module A = "a.js";

is roughly equivalent to:

var A = require("./a");

So that part of the problem doesn't look too bad. But the migration
path to something like Simple Modules needs more research. One thing
that may present problems, particularly for Wrappings, but for
CommonJS modules in general, is the fact that "module" is a keyword in
Simple Modules. An expression like module.declare or module.id would
cause a syntax error in Simple Modules. This is something that I'm
currently looking into.

Another item that will (almost) surely be different: module ids and
paths. As you can see from the above example, in Simple Modules paths
are relative unless they are rooted or absolute. And the file
extension is part of the module locator. This model is a more natural
fit for javascript on the web and I imagine that if Simple Modules get
into ES6, it will be will these path conventions.

Kevin


On Dec 28, 7:58 pm, Wes Garland <w...@page.ca> wrote:
> James;
>
> I skimmed your blog and what I took away was that you are concerned about is
> that CommonJS wrapped modules have a different initialization semantics than
> the simple modules proposal, and as such you believe that module factory
> functions should be invoked during the dependency-resolution phase, rather
> upon the first use of the require() call.
>
> There is only one place where this difference leaks out, and that is during
> the execution of code in the main body of the module. Availability of
> exports is a non-issue
>
> This was discussed at length a couple of years ago on this list. In the era
> of the "pythonic modules" thread.  Do you remember why the standard was
> written the way it was? Have any of those reasons changed?
>
> Are you also certain that simple modules require eager evaluation of the
> module declaration?
>
> Also, aside from semantic compatibility with the simple modules proposal,
> are there any benefits to eager evaluation of module factory functions?
> There are definitely disadvantages, such as
>
>    - breaks semantic compatibility with existing CommonJS modules
>    - cpu cycles and memory consumed for modules which may never be needed
>    - prevents server-side implementations from optimizing away unnecessary

Wes Garland

unread,
Dec 28, 2010, 10:49:31 PM12/28/10
to comm...@googlegroups.com
On the plus side, simple-modules are strictly opt-in: you are not going to be accidentally trying to use CommonJS moduels in a simple modules environment.

It is my hope that will be able to develop simple modules=>CommonJS automatic translation tools without significant difficulty, provided that the simple module is written using only ES5.

I also think we will be able to develp CommonJS=>simple modules tools, although there are many problems, as you've pointed out.  That said, I don't think the collision is a big one; that will be a piece of cake to patch up.  The module identifier semantics issues you've raised, OTOH, are a much tougher nut to crack.

khs4473

unread,
Dec 28, 2010, 11:15:09 PM12/28/10
to CommonJS
A further explanation of the module path/identifier issue for anyone
interested: in CommonJS modules, module identifiers are used for both
a) built-in system-defined libraries and b) relatively-positioned
module files. That's why we have to distinguish relative paths using
"./" and why we leave off the file extension. But in Simple Modules,
these concerns are separated out. Relatively-positioned module files
are brought in using an external module declaration:

module M = "path/to/m.js";

and built-in system-defined libraries would be brought in something
like this:

import stdlib.*;

At least that's how I currently understand things.

James Burke

unread,
Dec 29, 2010, 12:05:06 AM12/29/10
to comm...@googlegroups.com
On Tue, Dec 28, 2010 at 4:58 PM, Wes Garland <w...@page.ca> wrote:
> Are you also certain that simple modules require eager evaluation of the
> module declaration?

While the semantics may not be completely eager evaluation, they seem
closer to eager/immediate evaluation than availability semantics. I
confirmed when I talked in person to Dave Herman that "import" and
"module" cannot be used in this way:

if (someCondition) {
module a = "a.js";
} else {
module a = "a1.js";
}

It appears that all "import" and "module" uses must effectively be at
the top of a module (or hoisted). That looks more like immediate
evaluation of the dependency vs availability. I'm sure there are some
subtle differences, and perhaps I do not have the right terminology,
but the following forms which are allowed in CommonJS modules would
not be allowed in Simple Modules (these are real world cases I have
hit when trying to convert modules written for Node):

var a;
if (someCondition) {
a = require("a");
} else {
a = require("a1");
}

try {
var a = require("a");
} catch (e) {
//Do something for old version of node.
}

This is the problem with availability semantics that I was trying to
explain in the blog post. Of course I encourage others to confirm with
a reading of the Simple Modules strawman. There is always the
possibility I got some of the nuance wrong or just plain misread it.

It will be possible to make dynamic decisions in code then load code
via the module loaders strawman, but that syntax looks a lot like the
require() callback style as used in RequireJS -- a callback is needed
and the loaded modules are passed as arguments to the callback
function. That likely changes the expectations of the code as written
above that it would not be a straightforward port.

> Also, aside from semantic compatibility with the simple modules proposal,
> are there any benefits to eager evaluation of module factory functions?
> There are definitely disadvantages, such as
>
> breaks semantic compatibility with existing CommonJS modules

Right, that is my main point -- simple modules will break semantic
compatibility CommonJS modules. If simple modules are the future, then
best to change the expectation now in an wrapped format while CommonJS
modules are still relatively young than to give more time to create
more modules that will not have the same semantics. It also fits with
how scripts on the web work today too, so it is not without precedent.

> cpu cycles and memory consumed for modules which may never be needed
> prevents server-side implementations from optimizing away unnecessary I/O
> from unused modules

These would both seem to apply to Simple Modules too. It seems to me a
server side implementation would need more direct hints to optimize
away I/O, particularly for browser-run modules where the environment
will not be known to the server. Those same kinds of hints could be
used for completely removing a reference to a dependency. Immediate
evaluation is how browser scripts work today, so I do not believe
these to be a significant cost, and optimization strategies are
possible.

James

khs4473

unread,
Dec 29, 2010, 12:07:34 AM12/29/10
to CommonJS
James and Wes,

Would you guys be willing to work with me (and whoever else wants to
participate) in hammering out the common ground between AMD and
Wrappings? I feel like maybe we can come to an agreement on a small
but essential core that will enable us all to move forward in an
interoperable way. Ideally we would end up taking a vote and putting
a CommonJS stamp on something.

What do you think?
Kevin

James Burke

unread,
Dec 29, 2010, 1:41:09 AM12/29/10
to comm...@googlegroups.com
On Tue, Dec 28, 2010 at 9:07 PM, khs4473 <khs...@gmail.com> wrote:
> Would you guys be willing to work with me (and whoever else wants to
> participate) in hammering out the common ground between AMD and
> Wrappings?  I feel like maybe we can come to an agreement on a small
> but essential core that will enable us all to move forward in an
> interoperable way.  Ideally we would end up taking a vote and putting
> a CommonJS stamp on something.

The goal is to try to come to some common understanding, and that was
the reason for me posting the link and the concern about compatibility
with Simple Module semantics. I think it is hard one to overcome given
that some on this list want to try to keep as much CommonJS semantics
as possible, but there are others on this list who have implemented
AMD and for me in particular moved forward with implementation to see
if the market and a broader sampling of people can help provide input
into the discussion.

Really the choices are not that much different, more personal style
preferences with neither one having significant weaknesses. In that
case, it may be best for the market to decide. But I am open to
talking more about it. I just don't see where any movement will
happen. For me in particular:

* I prefer immediate execution, seems to fit better with Simple
Modules and how the web works today.
* I want the dependencies to line up with factory function arguments
to allow injection, to reduce typing cost. Along those lines I prefer
grandfathering in "require" "module" and "exports" as special
dependency names from CommonJS modules to allow an easier transition
to a wrapped format.
* Function.toString parsing is available for those that do not want to
code a dependency array, but it is best if that model follows
immediate execution semantics, given the problems mentioned above with
try/catch and if/else use of require calls.

James

Sander Tolsma

unread,
Dec 29, 2010, 5:51:20 AM12/29/10
to comm...@googlegroups.com
James,

> It appears that all "import" and "module" uses must effectively be at
> the top of a module (or hoisted).

I don't extract that in the simple_modules strawman.. It says: "Module
declarations are only allowed at the top level of a script or module, but
for convenience, they can nest within top-level blocks, and are hoisted to
be in scope for the entire containing script or module."

Maybe someone can confirm but I interpreted this as "Module declarations are
only allowed as top level statements of a script or module", so following is
allowed in my opinion:

<script type="harmony">
module Math {
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
}
</script>

<script type="harmony">
// we can import in script code, not just inside a module
import Math.{sum, pi};

var TwoPi = sum(pi, pi);

<..all other kind of code...>

module Counter {
var counter = 0;
export function increment() { return counter++ }
export function current() { return counter }
}

<..all other kind of code using all previous import and module
declarations...>

</script>

Using module and import in if () {} else {} or while etc construction is not
top level use and so by the definition not allowed...

I will come with a more lengthy reply to your blog post later on because I
think it is impossible to solve cyclic dependencies between modules in your
proposal, the simple_modules proposal can solve that because it builds the
exports at compile time (something that can't be done at runtime, or
scanning the code beforehand is needed)...

Regards,

Sander


khs4473

unread,
Dec 29, 2010, 9:48:44 AM12/29/10
to CommonJS
Side question: What versions of Opera Mobile does
Function.prototype.toString not work for? I've tried it with the
emulators/simulators for Mobile and Mini and they seem to work
correctly.

Kris Zyp

unread,
Dec 29, 2010, 4:56:17 PM12/29/10
to comm...@googlegroups.com, James Burke
I believe there is a false dichotomy here and the order of execution has
already been clearly defined. When an AMD module lists a module in the
dependency array, it has to execute the target module's factory prior to
this module's factory in order to get and provide the correct export
(since the export object can be replaced with a return or
module.exports). When you listing dependencies, there isn't really
another other choices for when to execute dependencies. James called
this the "execution" approach, although I don't even know what the
"availability" approach would mean here.

When an module factory executes, and it make a synchronous require()
call, and the target module id's factory has not been executed yet, it
must immediately began executing that target factory (before the
require() call returns). This behavior has been clearly established by
CommonJS. A require() that doesn't immediately execute the module's
factory when that module's factory that has not been executed yet is not
a CommonJS require(). This corresponds to the "availability" approach
(in the sense that require() calls can determine when an available
factory gets executed).

We could make this behavior more explicitly spelled out in the
specification, but I had thought this could easily be deduced from the
requirements, I don't see any other consistent way to interpret the
specs. But I'd be glad to add some wording to the AMD to make this clear.

Thanks,
Kris

Sander Tolsma

unread,
Dec 29, 2010, 5:13:58 PM12/29/10
to comm...@googlegroups.com
Kris,

>I believe there is a false dichotomy here and the order of execution has

..snip..

>(in the sense that require() calls can determine when an available
>factory gets executed).

So (if I understand you correctly) if dependencies are NOT injected in the
factory functions argument list (as also discussed on the list as a possible
middle ground solution) but are 'required' as you describe, then the factory
function doesn't need to be executed before the 'parent' module factory
function is called... Am I right? Is this kind of implementation then
compatible to the simple_module draft or does it still give problems? My
opinion is that it is compatible but I'd like to have the opinion of James
too...

Regards,

Sander

Kris Zyp

unread,
Dec 29, 2010, 5:26:04 PM12/29/10
to comm...@googlegroups.com, Sander Tolsma, Dave Herman

Not sure what you mean, maybe you could give an example.

As far as simple module compatibility, it is really hard to compare
since simple modules has a compile time linking that isn't really
equatable to the runtime models of "execution" vs "availability". For
instance, I believe the following would work with simple modules (cc'ing
Dave Herman to make sure):

module A {
export function foo() {
}
import B.bar;
bar();
}

module B {
export function bar() {
}
import B.foo;
foo();
}

This is like the "execution" approach in the way that circular
references can be created at compile time, but like the "availability"
approach in terms of runtime execution.

--
Thanks,
Kris

Sander Tolsma

unread,
Dec 29, 2010, 6:04:21 PM12/29/10
to comm...@googlegroups.com, Dave Herman
Kris,

For example (doesn't matter what the name of the define/declare function is,
as James already stated):

a.js:

define/module.declare(['./b'], function(require, exports, module) {
var bar;
exports.foo = function(){};
bar = require('./b').bar;
bar();
})

b.js:

define/module.declare(['./a'], function(require, exports, module) {
var foo;
exports.bar = function(){};
foo = require('./b').foo;
foo();
})

In this example the loader makes sure './a' and './b' are loaded but the
factory function of './a' will be called when the require function in 'b' is
called (or the other way around, that depends on which module is used first
;-)). In that way the exports of './b' already has bar defined and so this
can be used in './a' module after requiring './b'. So the implementation is
as defined by the modules/1.1.1 draft and almost compatible to the simple
module draft except that in the simple module implementations all exports of
the module are available even the ones defined after the import statement
because of the compile time linking of the export.

The simple module example you gave is exactly how I did interpreted it
myself and it is totally compatible with what I defined above as example. I
hope Dave can agree with your example...

Regards,

Sander

David Herman

unread,
Dec 29, 2010, 6:14:13 PM12/29/10
to Kris Zyp, comm...@googlegroups.com, Sander Tolsma
[resending to include group -- sorry for the duplicate messages]

> As far as simple module compatibility, it is really hard to compare
> since simple modules has a compile time linking that isn't really
> equatable to the runtime models of "execution" vs "availability". For
> instance, I believe the following would work with simple modules (cc'ing
> Dave Herman to make sure):
>
> module A {
> export function foo() {
> }
> import B.bar;
> bar();
> }
>
> module B {
> export function bar() {
> }
> import B.foo;
> foo();
> }

The only part of this that doesn't work is the fact that modules are evaluated in order, so they can't call each other's functions at module top-level as you have here. To be clear, there's no compile-time error; everything links up just fine. But there's a runtime error because module A calls bar() before module B has been evaluated, so B.bar is not yet initialized.

But as long as the references are delayed via `function' there won't be a runtime error. For example:

module A {
export function foo() {
}
import B.bar;

export function go() { bar() }
}

module B {
export function bar() {
}

import A.foo;
export function go() { foo() }
}

A.go();
B.go();

Dave

Kris Zyp

unread,
Dec 29, 2010, 6:33:05 PM12/29/10
to comm...@googlegroups.com, Sander Tolsma

On 12/29/2010 4:04 PM, Sander Tolsma wrote:
> Kris,
>

> For example (doesn't matter what the name of the define/declare function is,
> as James already stated):
>
> a.js:
>
> define/module.declare(['./b'], function(require, exports, module) {
> var bar;
> exports.foo = function(){};
> bar = require('./b').bar;
> bar();
> })
>
> b.js:
>
> define/module.declare(['./a'], function(require, exports, module) {
> var foo;
> exports.bar = function(){};
> foo = require('./b').foo;
> foo();
> })
>
> In this example the loader makes sure './a' and './b' are loaded but the
> factory function of './a' will be called when the require function in 'b' is
> called (or the other way around, that depends on which module is used first
> ;-)). In that way the exports of './b' already has bar defined and so this
> can be used in './a' module after requiring './b'.

Exactly. And I believe the reference unit tests Kris Kowal created
enforce this behavior too.

--
Thanks,
Kris

James Burke

unread,
Dec 29, 2010, 10:07:19 PM12/29/10
to comm...@googlegroups.com
On Wed, Dec 29, 2010 at 2:51 AM, Sander Tolsma <goo...@tolsma.net> wrote:
> I will come with a more lengthy reply to your blog post later on because I
> think it is impossible to solve cyclic dependencies between modules in your
> proposal, the simple_modules proposal can solve that because it builds the
> exports at compile time (something that can't be done at runtime, or
> scanning the code beforehand is needed)...

Using exports is allowed in AMD, and therefore cyclic dependencies are
possible. There is not a full range of possibility: in CommonJS you
can partially initialize an exports in one module, then require the
other one that is a cyclic dependency, and that allows some other
partial bindings, but to me that is a very small advantage and very
brittle: a refactor of the code to move code up or down in the module
could break.

So cyclic dependencies are possible, and my AMD implementation passes
the cyclical test that was part of the CommonJS Modules test set. The
ones that are possible are the ones like Simple Modules: if you
reference the cyclic module properties in function calls, it works
out. You can also use the sync require("") call to get a handle on a
module inside a function, that opens up some cyclical use even when
exports is not in play (the cyclical module uses return to define the
exported value).

James


James

James Burke

unread,
Dec 29, 2010, 10:08:32 PM12/29/10
to comm...@googlegroups.com
On Wed, Dec 29, 2010 at 6:48 AM, khs4473 <khs...@gmail.com> wrote:
> Side question:  What versions of Opera Mobile does
> Function.prototype.toString not work for?  I've tried it with the
> emulators/simulators for Mobile and Mini and they seem to work
> correctly.

If you are using the very latest Opera Mobile it may be fixed. When I
asked on es-discuss about it, I believe someone in the know said the
the version of Opera Mobile in development at the time would have a
usable toString. But I do not have any other info on the version.

James

James Burke

unread,
Dec 30, 2010, 12:01:51 AM12/30/10
to comm...@googlegroups.com
On Wed, Dec 29, 2010 at 1:56 PM, Kris Zyp <kri...@gmail.com> wrote:
> I believe there is a false dichotomy here and the order of execution has
> already been clearly defined. When an AMD module lists a module in the
> dependency array, it has to execute the target module's factory prior to
> this module's factory in order to get and provide the correct export
> (since the export object can be replaced with a return or
> module.exports). When you listing dependencies, there isn't really
> another other choices for when to execute dependencies. James called
> this the "execution" approach, although I don't even know what the
> "availability" approach would mean here.
>
> When an module factory executes, and it make a synchronous require()
> call, and the target module id's factory has not been executed yet, it
> must immediately began executing that target factory (before the
> require() call returns). This behavior has been clearly established by
> CommonJS. A require() that doesn't immediately execute the module's
> factory when that module's factory that has not been executed yet is not
> a CommonJS require(). This corresponds to the "availability" approach
> (in the sense that require() calls can determine when an available
> factory gets executed).

This second paragraph is what I mean by "availability": in previous
discussions of a Wrappings thing that was module.declare, I understood
it to mean this:

//Fetch a.js, but do not call the factory function.
module.declare(["./a"], function (require, exports, module) {

if (someCondition) {
//a's module factory function is executed here (if
//no other module called on require for it already)
var localA = require("./a");
}
});

Only make sure to download a.js. Yes, a.js is evaluated, *but* the
module's factory function is not called before the factory function
above is called. a's factory function is only called during the
assignment to localA.

My terminology could be better, and there are some different things I
grouped into the "availability" word:
1) delayed execution of module factory function until first sync
require("") call.
2) In existing CommonJS modules, the tendency to use delayed execution
of a dependency until the require call to do conditional logic,
try/catch around modules. This works out in sync require environments
since file I/O is also delayed until require is called.

In Simple Modules, since all import and module calls cannot exist in
conditional flow control (at least imports are hoisted to the top if
not already), I read this as being roughly equivalent of hoisting all
sync require("") calls to the top of a CommonJS module. That would
break assumptions used by existing CommonJS modules and it is
different semantics, more closer to how AMD operates, where the
dependency's factory functions are executed "at the top", before
executing the current module's factory function.

For #2 above, this is actually a problem for all the Wrappings
proposal, and is probably enough of a change from the allowed CommonJS
Modules semantics to be a problem -- a Wrappings proposal would need
to fetch the files for all modules referenced via require(""), but the
examples I gave with the try/catch and the if/else (particularly if
that if/else is if(node.version ==2 ) require("modV2"); else
require("modV3")) would be a problem/an error.

So:
1) Existing CommonJS Modules 1.1 semantics cannot all be maintained in
a wrapped format, so it is less important to try to maintain strict
semantic compatibility.
2) Executing dependency's factory functions before executing the
current module fits better with Simple Modules, and it is how the web
works today.

These two things drive the main difference between AMD and what I have
heard of the other Wrappings proposals. If you are fine with the above
two conclusions, then AMD makes sense as the wrapped module format.

James

Wes Garland

unread,
Dec 30, 2010, 8:40:03 AM12/30/10
to comm...@googlegroups.com, Sander Tolsma
> Exactly. And I believe the reference unit tests Kris Kowal created
> enforce this behavior too.

Indeed they do. I tested this recently, in fact.

Sander Tolsma

unread,
Dec 30, 2010, 10:20:36 AM12/30/10
to comm...@googlegroups.com
James,

<snip first part>


> Only make sure to download a.js. Yes, a.js is evaluated, *but* the
> module's factory function is not called before the factory function
> above is called. a's factory function is only called during the
> assignment to localA.

Agreed as the current Modules specs state this..

> My terminology could be better, and there are some different things I
> grouped into the "availability" word:
> 1) delayed execution of module factory function until first sync
> require("") call.
> 2) In existing CommonJS modules, the tendency to use delayed execution
> of a dependency until the require call to do conditional logic,
> try/catch around modules. This works out in sync require environments
> since file I/O is also delayed until require is called.

Ahh, I think this 2nd point has nothing to do with delayed execution and
"availability" etc because this is also not allowed in the "wrappings"
proposal! The factory function isn't called if one of the modules in the
dependency list doesn't exists and can't be loaded!! Let me give an example:

Module.declare(['./a','a1'], function(require, exports, module){
var b;
if (someCondition) {
b = require('./a');
} else {
b = require('a1');
}
});

As you said this is not 'compatible' with Simple Modules but this can be
rewritten to:

Module.declare(['./a','a1'], function(require, exports, module){
var b,
a = require('./a'),
a1 = require('a1');
if (someCondition) {
b = a;
} else {
b = a1;
}
});

And this IS compatible with Simple Modules... If 'a' or 'a1' don't exist the
factory function will not be called because not all dependencies are
satisfied. And you will get an compile error in Simple Modules because it
doesn't find all mentioned modules...

If you want to 'require' a module not mentioned in the dependency list and
it is not available then the current specs say that an error is thrown. This
is incompatible with Simple modules because moduleLoader.getModule(name)
will return null if the module doesn't exists. If we change that requirement
in modules/1.1.1 to returning null instead of throwing (or maybe adding a
extra function to make transformation from CommonJS modules to Simple
modules easier), CommonJS Wrappings with changed Modules/1.1.1 is compatible
with (and in my mind even easily transformable to) simple modules.

CommonJs wrapped version:

Module.declare(['./a'], function(require, exports, module){
var b,
a1 = givebackmoduleornull('a1'),
a0 = require('./a');
if (a1==null) {
b = a0;
} else {
b = a1;
}
});

Simple module version:

var b,
a1 = moduleLoader.getModule('a1');
module a0 = a;
if (a1==null) {
b = a0;
} else {
b = a1;
}

> In Simple Modules, since all import and module calls cannot exist in
> conditional flow control (at least imports are hoisted to the top if
> not already), I read this as being roughly equivalent of hoisting all
> sync require("") calls to the top of a CommonJS module. That would
> break assumptions used by existing CommonJS modules and it is
> different semantics, more closer to how AMD operates, where the
> dependency's factory functions are executed "at the top", before
> executing the current module's factory function.

For Simple Modules you need to split the execution in two phases: phase1 is
the compile time, phase2 is evaluation time. As Dave responded, evaluation
is done in order and his response on Kris example shows that the other
module doesn't need to be evaluated to be imported (that's done at compile
time) but the other module exports needs to be initialized/evaluated to be
used else you get an runtime error! Exactly as the CommonJS module specs are
describing...

> For #2 above, this is actually a problem for all the Wrappings
> proposal, and is probably enough of a change from the allowed CommonJS
> Modules semantics to be a problem -- a Wrappings proposal would need
> to fetch the files for all modules referenced via require(""), but the
> examples I gave with the try/catch and the if/else (particularly if
> that if/else is if(node.version ==2 ) require("modV2"); else
> require("modV3")) would be a problem/an error.

I don't think it is that a big problem, see my previous remarks..

> So:
> 1) Existing CommonJS Modules 1.1 semantics cannot all be maintained in
> a wrapped format, so it is less important to try to maintain strict
> semantic compatibility.
> 2) Executing dependency's factory functions before executing the
> current module fits better with Simple Modules, and it is how the web
> works today.
>
> These two things drive the main difference between AMD and what I have
> heard of the other Wrappings proposals. If you are fine with the above
> two conclusions, then AMD makes sense as the wrapped module format.

Your remarks are a good input but I'm not convinced that AMD is the only way
forward. I'm even more convinced that with the above mentioned small changes
wrappings like code is more suited to be transformed to simple modules...

Regards,

Sander

James Burke

unread,
Dec 30, 2010, 2:18:30 PM12/30/10
to comm...@googlegroups.com
On Thu, Dec 30, 2010 at 7:20 AM, Sander Tolsma <goo...@tolsma.net> wrote:
> As you said this is not 'compatible' with Simple Modules but this can be
> rewritten to:
>
> Module.declare(['./a','a1'], function(require, exports, module){
>    var b,
>        a = require('./a'),
>        a1 = require('a1');
>    if (someCondition) {
>        b = a;
>    } else {
>        b = a1;
>    }
> });
>
> And this IS compatible with Simple Modules... If 'a' or 'a1' don't exist the
> factory function will not be called because not all dependencies are
> satisfied. And you will get an compile error in Simple Modules because it
> doesn't find all mentioned modules...

Right, and at that point, the rewritten code is effectively what
happens in AMD -- the dependencies are evaluated before the current
module factory function can do its real work. The discrepancies
between AMD and wrappings then become:

1) what to call the entry point, module.declare or define. That is a
bikeshed, and might as well go with the one that has some adoption.

2) Whether or not to allow lining up of dependency array items to
factory function arguments. Once you have to specify the dependencies
in the array, then to avoid the extra typing of then saying
require("") inside the function (at the top of the function), then it
makes sense to do that.

For those who do not want to mess with that, there is this convenience
form that uses Function.prototype.toString() to pull out the
dependencies:

define(function(require, exports, module) {
var a = require("a"),


a1 = require("a1");

});

Most modules don't need module and in AMD return can be used to set
exports, so for most modules, this can be shortened to:

define(function(require) {
var a = require("a"),


a1 = require("a1");

});

> If you want to 'require' a module not mentioned in the dependency list and
> it is not available then the current specs say that an error is thrown. This
> is incompatible with Simple modules because moduleLoader.getModule(name)
> will return null if the module doesn't exists. If we change that requirement
> in modules/1.1.1 to returning null instead of throwing (or maybe adding a
> extra function to make transformation from CommonJS modules to Simple
> modules easier), CommonJS Wrappings with changed Modules/1.1.1 is compatible
> with (and in my mind even easily transformable to) simple modules.

This can be changed in AMD too, use null instead of throw an error.
This seems like a minor point, but a good clarification on the
behavior in Simple Modules.

James

khs4473

unread,
Dec 30, 2010, 2:45:53 PM12/30/10
to CommonJS
> 1) what to call the entry point, module.declare or define. That is a
> bikeshed, and might as well go with the one that has some adoption.

Well... module.declare was chosen because it causes the least impact
to established server systems. "module" is already there and scoped
properly. "define" would require server systems to introduce (yet)
another module-scoped variable.

James Burke

unread,
Dec 30, 2010, 3:06:15 PM12/30/10
to comm...@googlegroups.com
On Thu, Dec 30, 2010 at 11:45 AM, khs4473 <khs...@gmail.com> wrote:
>> 1) what to call the entry point, module.declare or define. That is a
>> bikeshed, and might as well go with the one that has some adoption.
>
> Well...  module.declare was chosen because it causes the least impact
> to established server systems.  "module" is already there and scoped
> properly.  "define" would require server systems to introduce (yet)
> another module-scoped variable.

In the browser with script-src loading it would be a global name. It
seems odd to have "module" with just a declare() function on it on
this case, it increases the typing that has to be done by all
developers vs asking an engine implementor to create a new variable.

Also, using module also seems to conflict with Simple Modules? Even if
it would not technically cause an error (if it is a conditional
keyword), it seems best to avoid developer confusion about it.

James

khs4473

unread,
Dec 30, 2010, 3:45:04 PM12/30/10
to CommonJS
> In the browser with script-src loading it would be a global name.

Right, but this is an implementation detail and should be transparent
to the user. In fact, in FlyScript I actually restore "module" and
"declare" to their original values (usually undefined) when the loader
isn't actively loading anything.

Regarding the typing, we'd have to take a poll, but I for one think
"module.declare" looks and feels rad. And trust me, I have to look at
it a lot! :)

Regarding "module" conflicting with a keyword in Simple Modules, if
it's an issue then it's an issue for Modules 1.1 in general. I'm
wondering if that ship hasn't already set sail, if you get my meaning.


On Dec 30, 3:06 pm, James Burke <jrbu...@gmail.com> wrote:

James Burke

unread,
Dec 30, 2010, 4:51:52 PM12/30/10
to comm...@googlegroups.com
On Thu, Dec 30, 2010 at 12:45 PM, khs4473 <khs...@gmail.com> wrote:
> Regarding "module" conflicting with a keyword in Simple Modules, if
> it's an issue then it's an issue for Modules 1.1 in general.  I'm
> wondering if that ship hasn't already set sail, if you get my meaning.

Modules 1.1 was best effort that was done by a subset of people that
want to work with JavaScript trying to standardize something that was
not part of the language but using existing the existing language. It
did a great job at identifying some useful things, but wide scale use
of it only cropped up in the last year with Node.

It did not get a good story for script src loading in the most widely
deployed JS environment for one, and it is the reason we are having
this discussion. A wrapped format does change the expectations from
Modules 1.1 already, given the examples on this thread. In the
meantime, Simple Modules is gaining more momentum in the ECMAScript
arena.

In other words, more information is now available than when Modules
1.1 was constructed. It should be expected that some things should
change. The ship may be able to sail in the bay but not on the ocean.

But this is a bikeshed argument, and very stylistic, so I will try to
avoid posting more about the name. Suffice to say, define works just
as well as module.declare.

James

Wes Garland

unread,
Dec 30, 2010, 6:29:24 PM12/30/10
to comm...@googlegroups.com
On Thu, Dec 30, 2010 at 3:45 PM, khs4473 <khs...@gmail.com> wrote:
Regarding "module" conflicting with a keyword in Simple Modules, if
it's an issue then it's an issue for Modules 1.1 in general.  I'm
wondering if that ship hasn't already set sail, if you get my meaning.

I agree.

Besides which, I can't see anybody using CommonJS to load harmony modules, and you certainly would not want to run CommonJS modules inside harmony, as the two are not source-code compatible.

I think we should have our eyes set firmly on simple modules, but we should be looking at semantic rather than syntactic compatibility.  Syntax is relatively easy to fix up with machine translation, whereas semantic translation / automated refactoring is a much harder problem.

Wes

On Dec 30, 3:06 pm, James Burke <jrbu...@gmail.com> wrote:
> On Thu, Dec 30, 2010 at 11:45 AM, khs4473 <khs4...@gmail.com> wrote:
> >> 1) what to call the entry point, module.declare or define. That is a
> >> bikeshed, and might as well go with the one that has some adoption.
>
> > Well...  module.declare was chosen because it causes the least impact
> > to established server systems.  "module" is already there and scoped
> > properly.  "define" would require server systems to introduce (yet)
> > another module-scoped variable.
>
> In the browser with script-src loading it would be a global name. It
> seems odd to have "module" with just a declare() function on it on
> this case, it increases the typing that has to be done by all
> developers vs asking an engine implementor to create a new variable.
>
> Also, using module also seems to conflict with Simple Modules? Even if
> it would not technically cause an error (if it is a conditional
> keyword), it seems best to avoid developer confusion about it.
>
> James

--
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.

Hannes Wallnoefer

unread,
Dec 30, 2010, 8:21:20 PM12/30/10
to comm...@googlegroups.com
2010/12/28 James Burke <jrb...@gmail.com>:
> I wrote up a blog post relating to AMD and the more recent Wrappings
> proposal here:
> http://tagneto.blogspot.com/2010/12/standards-and-proposals-for-javascript.html
>
> My main concern with Wrappings is that the availability semantics do
> not translate well to the future with Simple Modules.

I can't help feeling that future compatibility with Simple Modules is
a bit of a red herring. Yes, you can probably find some examples of
conditional require calls out there in the wild, just like you can
with any other anti-pattern. IMO that's not enough to disqualify the
idea of loading modules with require(). I don't think this style of
coding has ever been propagated as a benefit of CommonJS modules
anywhere, and it should suffice to educate people about possible
consequences it entails, including incompatibility with automatic
dependency detection and future standards.

One potential problem I do see with AMD is that it may not be thinking
big enough. Keeping a list of module ids and function parameters in
sync may be easy for a small number of dependencies, but what about
modules that depend on 15, 30, or 50 other modules? These numbers may
seem large by the standards of today's browser applications, but if
you look at other programming environments they are quite common even
for medium size projects. Having to keep that many function parameters
in sync with the dependency list may be difficult, and misalignments
in the two lists may be hard to debug. Note that I haven't tried it
yet, it may actually be doable or even easy. It's just something to
keep in mind.

Hannes

James Burke

unread,
Dec 30, 2010, 10:26:22 PM12/30/10
to comm...@googlegroups.com
On Thu, Dec 30, 2010 at 5:21 PM, Hannes Wallnoefer <han...@helma.at> wrote:
> One potential problem I do see with AMD is that it may not be thinking
> big enough. Keeping a list of module ids and function parameters in
> sync may be easy for a small number of dependencies, but what about
> modules that depend on 15, 30, or 50 other modules? These numbers may
> seem large by the standards of today's browser applications, but if
> you look at other programming environments they are quite common even
> for medium size projects. Having to keep that many function parameters
> in sync with the dependency list may be difficult, and misalignments
> in the two lists may be hard to debug. Note that I haven't tried it
> yet, it may actually be doable or even easy. It's just something to
> keep in mind.

This style is supported in AMD for those cases
(Function.prototype.toString() is used to find the dependencies):

define(function(require){
var a = require("a"),

b = require("b"),
....
z = require("z")

return {};
});

James

Reply all
Reply to author
Forward
0 new messages