require.setExports()

56 views
Skip to first unread message

Isaac Schlueter

unread,
Dec 14, 2009, 3:31:58 AM12/14/09
to nod...@googlegroups.com, comm...@googlegroups.com
While it's nice and predictable to return things on an exports object,
it leads to this pattern, which is somewhat ugly:

var Foo = require("foo").Foo;
var myFoo = new Foo; // whoa, that's a lotta foo!

Since modules so often define a single class, this would be much nicer:

var myFoo = new (require("foo"));

However, to do this, you have to return a function from a module,
instead of the "exports" object. This topic has been batted about in
CommonJS. In NodeJS, I ran into a problem where I wanted to have one
module "proxy" another module. That is, include the sub-module, and
export its exports object, transparently to the original caller.
Without setting the exports object entirely, there's no way to do that
without an ugly for/in loop that copies the members from one object to
the other.

NodeJS has this functionality because the "module" object is exposed
to the module code, so you can do "module.exports = foo", and export
that new object instead of the original exports object. However, this
opens up some weird issues. Kris Kowal showed me a problem with that:
http://gist.github.com/255712

So, I propose the following:

1. require.setExports(newExports)

When called, it sets the exports object explicitly to the supplied
function. However, this is only allowed if the module's exports has
not already been acquired. Also, like setting module.exports, this
comes with the caveat that, once called, the old exports object is
useless. (Unless, of course, you then do require.setExports(exports)
or something.)

CommonJS folks: Care to bikeshed the name a bit? Think it should live
somewhere other than on require.setExports? Got any use cases that
break this approach? I've got it working now, so test cases would be
much appreciated.

nodejs specific: Let's remove the "module" reference from the node
module loader wrapper. It's not needed, and opens up a lot of weird
ways to get out of the box.

It's in my node "securable-setexports" branch.
http://github.com/isaacs/node/commits/securable-setexports/

--i

(For those of you in both NodeJS and CommonJS, sorry for the
crosspost. Seems relevant to both groups.)

Ash Berlin

unread,
Dec 14, 2009, 5:48:59 AM12/14/09
to comm...@googlegroups.com
On 14 Dec 2009, at 08:31, Isaac Schlueter wrote:

While it's nice and predictable to return things on an exports object,
it leads to this pattern, which is somewhat ugly:

var Foo = require("foo").Foo;
var myFoo = new Foo; // whoa, that's a lotta foo!

[snip]

Kris Kowal showed me a problem with that: http://gist.github.com/255712

[snip]


So, I propose the following:

1. require.setExports(newExports)

When called, it sets the exports object explicitly to the supplied
function.  However, this is only allowed if the module's exports has
not already been acquired.  Also, like setting module.exports, this
comes with the caveat that, once called, the old exports object is
useless.  (Unless, of course, you then do require.setExports(exports)
or something.)

CommonJS folks: Care to bikeshed the name a bit?  Think it should live
somewhere other than on require.setExports?  Got any use cases that
break this approach?  I've got it working now, so test cases would be
much appreciated.

I really like this aim and the caveat is well worth noting. However is there any reason the same behaviour couldn't be enforced by having a setter on the module.exports property that dies if its already been required? It certainly should be a property on the module object to my mind (rather than something on the require object).


nodejs specific: Let's remove the "module" reference from the node
module loader wrapper.  It's not needed, and opens up a lot of weird
ways to get out of the box.

The module object is part of the spec: http://wiki.commonjs.org/wiki/Modules/1.1 (I'm not sure what properties node.js puts on it, but the object itself should be there)

-ash

Hannes Wallnoefer

unread,
Dec 14, 2009, 6:11:58 AM12/14/09
to comm...@googlegroups.com
2009/12/14 Ash Berlin <ash_flu...@firemirror.com>:
>
> On 14 Dec 2009, at 08:31, Isaac Schlueter wrote:
>
> CommonJS folks: Care to bikeshed the name a bit?  Think it should live
> somewhere other than on require.setExports?  Got any use cases that
> break this approach?  I've got it working now, so test cases would be
> much appreciated.
>
> I really like this aim and the caveat is well worth noting. However is there
> any reason the same behaviour couldn't be enforced by having a setter on the
> module.exports property that dies if its already been required? It certainly
> should be a property on the module object to my mind (rather than something
> on the require object).

A simple and low-tech approach would be to just use a naming convention, like:

exports.__self__ = ...

require() would just check for the existence of the __self__ property
and return that instead of the exports object if it was defined. The
name "__self__" is just one possibility, of course - anything that is
very unlikely to collide with "ordinary" export names would work.

Hannes

Dean Landolt

unread,
Dec 14, 2009, 9:50:25 AM12/14/09
to comm...@googlegroups.com

Sure something like this would work, but it's still a hack -- any time the words "unlikely to collide" are used I get nervous. Not to mention how much bikeshedding will happen around the naming of this one special property. As an implementer what do you think about the above proposed approaches? Would either of them be easy enough to implement?

This limitation is the one issue with SecurableModules that seems to create the most backlash. It would be pretty cool to be able to ratify something (even if it's a wunderbar hack) that gets around this without introducing hazards.

Mark S. Miller

unread,
Dec 14, 2009, 10:21:25 AM12/14/09
to comm...@googlegroups.com, nod...@googlegroups.com
On Mon, Dec 14, 2009 at 12:31 AM, Isaac Schlueter <isaacsc...@gmail.com> wrote:
While it's nice and predictable to return things on an exports object,
it leads to this pattern, which is somewhat ugly:

var Foo = require("foo").Foo;
var myFoo = new Foo; // whoa, that's a lotta foo!

Since modules so often define a single class, this would be much nicer:

var myFoo = new (require("foo"));


Would

    var myFoo = new (require("foo").Foo)();

solve your original problem with no new mechanism?


--

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.





--
   Cheers,
   --MarkM

Kris Kowal

unread,
Dec 14, 2009, 2:50:01 PM12/14/09
to comm...@googlegroups.com
On Mon, Dec 14, 2009 at 7:21 AM, Mark S. Miller <eri...@google.com> wrote:
> Would
>     var myFoo = new (require("foo").Foo)();
> solve your original problem with no new mechanism?

I believe this is one of the problems that the new
module.setExports(exports) mechanism is intended to solve. It is
common practice in Ruby, where many of our immigrants have come from,
to have modules export a single class. There are times this makes
sense, and others where it does not, but this feature would make that
pattern more concise. It also flushes well with the pattern of types
that have auxiliary utilities like Object.freeze.

var URI = require("uri");
var uri = new URI();
var uri = uri.resolve(target);
var string = URI.resolve(source, target);

I do not think that using module.exports property assignment is a
viable variation since, unlike binary, this feature definitely should
be available to browser-based implementations because one of the
canonical problems it solves is:

var $ = require("jquery");
var _ = require("underscore");

module.setExports(exports) should return exports to enable this pattern:

exports = module.setExports({…});

This feature is also good for the pattern:

module.setExports(Object.freeze({…}));

We talked extensively about making it possible to have a module export
a single type some time ago, but the
non-identical-exports-returned-by-require problem got in the way.
Also, we did not foresee a way to have the module system host types
without "blessing" a type system at an architectural layer on which
that did not make sense. module.setExports(exports) solves both of
these problems.

I think we need to review the holistic implications of this feature,
but I do not presently foresee problems for safety, security, or
interoperability.

I am +1 on this feature. The cost in complexity to implementations is
not significant. The major caveat is resolved with strong normative
clauses that insist setExports must throw an error if the current
exports object have been given to any other module.

Also, in an attempt to forestall a lengthy digression, I am +1 on the
color of the bike shed.

Kris Kowal

George Moschovitis

unread,
Dec 14, 2009, 6:11:02 PM12/14/09
to CommonJS
looks useful, +1

-g.

PS: but,

var Foo = require("foo").Foo,
myFoo = new Foo()

is OK by me...



On Dec 14, 9:50 pm, Kris Kowal <cowbertvon...@gmail.com> wrote:

Isaac Z. Schlueter

unread,
Dec 14, 2009, 7:12:03 PM12/14/09
to CommonJS
On Dec 14, 7:21 am, "Mark S. Miller" <erig...@google.com> wrote:
> Would
> var myFoo = new (require("foo").Foo)();
> solve your original problem with no new mechanism?

That is possible now. However, it doesn't solve the issue with
proxying one module's exports from another. And, it's not quite as
pretty :)

Also, what Kris said.

Using a setter on module.exports isn't optimal, because it would be
best to support this now across engines that might not support
defineProperty yet.

On Dec 14, 11:50 am, Kris Kowal <cowbertvon...@gmail.com> wrote:
> module.setExports(exports) should return exports to enable this pattern:
> exports = module.setExports({…});
> This feature is also good for the pattern:
> module.setExports(Object.freeze({…}));

So, you prefer it to be hanging on module.setExports? That does kind
of make more sense, since the previous method (in node, at least) was
to manually set module.exports.

Unless there are any further suggestions, I say we go with that. It's
easy to implement, easy to use, doesn't rely on convention or
wunderbar hacks, doesn't require ES5 features, and when the assumption
is violated, it throws an error, so it's easy to spot the problem.

On Dec 14, 2:48 am, Ash Berlin <ash_flusspf...@firemirror.com> wrote:
> > nodejs specific: Let's remove the "module" reference from the node
> > module loader wrapper. It's not needed, and opens up a lot of weird
> > ways to get out of the box.
>
> The module object is part of the spec:http://wiki.commonjs.org/wiki/Modules/1.1
> (I'm not sure what properties node.js puts on it, but the object itself should be there)

I'm pretty sure that node is exposing more than it ought to. (You
could really cause some significant mischief by messing around with
it.)

I'll update the spec so that the "module" free var has a setExports
method, and update node to expose id and uri on it.

--i

Ash Berlin

unread,
Dec 14, 2009, 7:22:19 PM12/14/09
to comm...@googlegroups.com

On 15 Dec 2009, at 00:12, Isaac Z. Schlueter wrote:

> Using a setter on module.exports isn't optimal, because it would be
> best to support this now across engines that might not support
> defineProperty yet.

This shouldn't be a problem for any embedding - all of the JS engines have support for defining properties with getters from native code (I'm fairly sure the IE JS engine must, if anyone is using that. The other big 4 certainly do.) But as Kris pointed out this is something we need to support in a browser (where everything but IE has __define[GS]etter__. What would we do without IE to hate on?)

>
> On Dec 14, 11:50 am, Kris Kowal <cowbertvon...@gmail.com> wrote:
>> module.setExports(exports) should return exports to enable this pattern:
>> exports = module.setExports({…});
>> This feature is also good for the pattern:
>> module.setExports(Object.freeze({…}));
>
> So, you prefer it to be hanging on module.setExports? That does kind
> of make more sense, since the previous method (in node, at least) was
> to manually set module.exports.
>
> Unless there are any further suggestions, I say we go with that. It's
> easy to implement, easy to use, doesn't rely on convention or
> wunderbar hacks, doesn't require ES5 features, and when the assumption
> is violated, it throws an error, so it's easy to spot the problem.
>
> [snip]
>
> I'll update the spec so that the "module" free var has a setExports
> method, and update node to expose id and uri on it.
>
> --i

Sounds good.

While we are talking about modules: does someone fancy (re)starting discussion about the points listed on http://wiki.commonjs.org/wiki/Modules ?

-ash

Dean Landolt

unread,
Dec 14, 2009, 8:24:49 PM12/14/09
to comm...@googlegroups.com
On Mon, Dec 14, 2009 at 7:12 PM, Isaac Z. Schlueter <i...@foohack.com> wrote:
On Dec 14, 7:21 am, "Mark S. Miller" <erig...@google.com> wrote:
> Would
>     var myFoo = new (require("foo").Foo)();
> solve your original problem with no new mechanism?

That is possible now.  However, it doesn't solve the issue with
proxying one module's exports from another.  And, it's not quite as
pretty :)

To follow on Isaac's point I'll give a practical example: the UnitTest api. It's hard enough to bikeshed common method names -- to then have to agree on the name of the property to dangle on exports is just an unnecessary blocker to standardization. Isaac's notion of a proxy module would allow any system to provide an "assert" proxy module that points to any module with a UnitTest-conformant API and now we can all use code with a var assert = require("assert"); and move on with our lives. I think that's a pretty big benefit.

It also allows us to have modules with an already huge install base (like qunit) adapted without current users being forced to port everything. That's also pretty winful.
 

Also, what Kris said.

And that too...

Ryan Dahl

unread,
Dec 15, 2009, 7:25:12 AM12/15/09
to comm...@googlegroups.com, nod...@googlegroups.com
On Mon, Dec 14, 2009 at 9:31 AM, Isaac Schlueter
<isaacsc...@gmail.com> wrote:
> While it's nice and predictable to return things on an exports object,
> it leads to this pattern, which is somewhat ugly:
>
> var Foo = require("foo").Foo;
> var myFoo = new Foo; // whoa, that's a lotta foo!
>
> Since modules so often define a single class, this would be much nicer:
>
> var myFoo = new (require("foo"));
> [...]
> So, I propose the following:
>
> 1. require.setExports(newExports)

I've noticed people minimizing the usage of the "exports" object. EG

http://github.com/felixge/node-deferred/blob/5c6198c480475844db82a5c17cd63c94cc369a7b/lib/deferred.js#L9

The "exports" object could be removed entirely by following this pattern:

var MyModule = {};
MyModule.hello = "world";
MyModule.foo = "bar";
require.export(MyModule);

Advantages
- removes the special "exports" variable
- solves Isaac's problem of exporting classes
- easier to port non-commonjs code

Daniel Friesen

unread,
Dec 15, 2009, 10:18:12 AM12/15/09
to comm...@googlegroups.com
var MyModule = {};
MyModule.hello = "world";
MyModule.foo = "bar";
Object.merge(exports, MyModule);


~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Kevin Dangoor

unread,
Dec 15, 2009, 10:28:49 AM12/15/09
to comm...@googlegroups.com, nod...@googlegroups.com
On Tue, Dec 15, 2009 at 7:25 AM, Ryan Dahl <coldre...@gmail.com> wrote:
On Mon, Dec 14, 2009 at 9:31 AM, Isaac Schlueter
<isaacsc...@gmail.com> wrote:
> 1. require.setExports(newExports)

I've noticed people minimizing the usage of the "exports" object. EG

http://github.com/felixge/node-deferred/blob/5c6198c480475844db82a5c17cd63c94cc369a7b/lib/deferred.js#L9

I think that's largely because people are bringing along their current JS patterns (or, in the example above, their current code).

When writing new modules, I've found adding to exports to be easy and natural. It's also inline with some proposed syntax for ES Harmony.

export foo = 1;

I'll grant that, from my understand, modules in ES Harmony are not at all standardized yet. The working group is very interested in seeing how things work out for us.
 
The "exports" object could be removed entirely by following this pattern:

 var MyModule = {};
 MyModule.hello = "world";
 MyModule.foo = "bar";
 require.export(MyModule);

I don't really like this for two reasons:

1. require.export? just sounds funny.
2. this requires two extra lines in the "new common case" (apps built entirely around CommonJS modules).

exports.hello = "world";
exports.foo = "bar";

Advantages
- removes the special "exports" variable
- solves Isaac's problem of exporting classes
- easier to port non-commonjs code


The addition of module.setExports has the latter two advantages, and I actually consider the first to be a disadvantage for the "new common case"

Kevin

--
Kevin Dangoor

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

Mark S. Miller

unread,
Dec 15, 2009, 11:43:32 AM12/15/09
to comm...@googlegroups.com, nod...@googlegroups.com
On Tue, Dec 15, 2009 at 7:28 AM, Kevin Dangoor <dan...@gmail.com> wrote:
On Tue, Dec 15, 2009 at 7:25 AM, Ryan Dahl <coldre...@gmail.com> wrote:
On Mon, Dec 14, 2009 at 9:31 AM, Isaac Schlueter
<isaacsc...@gmail.com> wrote:
> 1. require.setExports(newExports)

I've noticed people minimizing the usage of the "exports" object. EG

http://github.com/felixge/node-deferred/blob/5c6198c480475844db82a5c17cd63c94cc369a7b/lib/deferred.js#L9

I think that's largely because people are bringing along their current JS patterns (or, in the example above, their current code).

When writing new modules, I've found adding to exports to be easy and natural. It's also inline with some proposed syntax for ES Harmony.

export foo = 1;

I'll grant that, from my understand, modules in ES Harmony are not at all standardized yet. The working group is very interested in seeing how things work out for us.
 
Indeed!


 
The "exports" object could be removed entirely by following this pattern:

 var MyModule = {};
 MyModule.hello = "world";
 MyModule.foo = "bar";
 require.export(MyModule);

I don't really like this for two reasons:

1. require.export? just sounds funny.
2. this requires two extra lines in the "new common case" (apps built entirely around CommonJS modules).

Also, 3. It doesn't handle cyclic module dependencies. The cyclic dependency problem is the motivation for initializing a provided object rather than returning a new object. Without the cyclic dependency problem, I agree it would be much better to (somehow) export by returning a newly constructed object.

 
exports.hello = "world";
exports.foo = "bar";

Advantages
- removes the special "exports" variable
- solves Isaac's problem of exporting classes
- easier to port non-commonjs code


The addition of module.setExports has the latter two advantages, and I actually consider the first to be a disadvantage for the "new common case"

Kevin

--
Kevin Dangoor

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

--

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.



--
   Cheers,
   --MarkM

Charles Jolley

unread,
Dec 15, 2009, 11:53:48 AM12/15/09
to comm...@googlegroups.com
> 1. require.setExports(newExports)

I've noticed people minimizing the usage of the "exports" object. EG

http://github.com/felixge/node-deferred/blob/5c6198c480475844db82a5c17cd63c94cc369a7b/lib/deferred.js#L9

I think that's largely because people are bringing along their current JS patterns (or, in the example above, their current code).



To me this seems like a special case of the "import module" I asked about last week.  module.setExports() solves the case for when you happen to only need to import one symbol instead of several.  

Given how often this comes up, it seems to me that CommonJS really needs to address this import option somehow if only as a transitional approach to help existing code move over to modules.

I also agree that exports is very natural when writing new code in the module pattern.

-C

Dean Landolt

unread,
Dec 15, 2009, 12:03:09 PM12/15/09
to comm...@googlegroups.com
Isaac and Kris have a proposal up on the wiki so show some hands...

http://wiki.commonjs.org/wiki/Modules/SetExports

Kris Zyp

unread,
Dec 15, 2009, 12:07:27 PM12/15/09
to comm...@googlegroups.com


Dean Landolt wrote:
> Isaac and Kris have a proposal up on the wiki so show some hands...
>
> http://wiki.commonjs.org/wiki/Modules/SetExports
I have long been in favor of being able to set the exports, so +1, but I
definitely would prefer it to be a setter (module.exports = myExports).
I don't think there are any real platforms that don't support setters
(and I certainly hope we aren't going to water down CommonJS with
attempts to run on IE).
Kris

Mark S. Miller

unread,
Dec 15, 2009, 12:13:28 PM12/15/09
to comm...@googlegroups.com
Microsoft has been the first to demonstrate (but not yet deploy) a full ES5 implementation. They demoed it working in an IE8. It, of course, has full support for the getters & setters mandated by ES5.

 
Kris

--

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.





--
   Cheers,
   --MarkM

Kris Zyp

unread,
Dec 15, 2009, 12:13:09 PM12/15/09
to comm...@googlegroups.com
> To prevent problems with inconsistencies in the case of circular
dependencies, if the module
> has already been required by another module, calling
module.setExports() must throw an Error

And I don't think this actually is worded correctly. Basically all
modules have been required by another module. Shouldn't it read:

To prevent problems with inconsistencies in the case of circular
dependencies, if the module's exports has already been returned by a
'require' call to another module, calling module.setExports() must throw
an Error

Kris

Ash Berlin

unread,
Dec 15, 2009, 12:19:52 PM12/15/09
to comm...@googlegroups.com

On 15 Dec 2009, at 17:13, Mark S. Miller wrote:

On Tue, Dec 15, 2009 at 9:07 AM, Kris Zyp <kri...@gmail.com> wrote:
Dean Landolt wrote:
> Isaac and Kris have a proposal up on the wiki so show some hands...
>
> http://wiki.commonjs.org/wiki/Modules/SetExports
I have long been in favor of being able to set the exports, so +1, but I
definitely would prefer it to be a setter (module.exports = myExports).
I don't think there are any real platforms that don't support setters
(and I certainly hope we aren't going to water down CommonJS with
attempts to run on IE).

Microsoft has been the first to demonstrate (but not yet deploy) a full ES5 implementation. They demoed it working in an IE8. It, of course, has full support for the getters & setters mandated by ES5.

Do you have a link to this? Last i checked IE8 only had getter/setter support when the object was a DOM object or something similar.

-ash

Howard Rauscher

unread,
Dec 15, 2009, 12:25:56 PM12/15/09
to comm...@googlegroups.com
Microsoft did it as a prototype. ES5 it is not built into the current releases of IE8

Howard Rauscher
how...@gmail.com
m: 214.957.1408


Zachary Carter

unread,
Dec 15, 2009, 12:27:44 PM12/15/09
to comm...@googlegroups.com

Mark S. Miller

unread,
Dec 15, 2009, 12:41:56 PM12/15/09
to comm...@googlegroups.com, Allen Wirfs-Brock, Pratap Lakshman (VJ#SDK)
[+Allen.Wirfs-Brock, pratapl]
Yes, that is a link only to for the DOM-only getters/setters already deployed. I am referring to the full ES5 JScript engine that MS demoed at an EcmaScript meeting -- many months ago -- running in an IE8. It is not yet released. IIRC, it was already passing the es5conform tests <http://es5conform.codeplex.com/>, the ES5 conformance test suites written and open sourced (BSD, yeah!) by Microsoft as part of this project.

Allen and Pratap, is there any link explaining what Microsoft already has working internally, or what was demoed, though not yet released?


--
   Cheers,
   --MarkM

Dean Landolt

unread,
Dec 15, 2009, 1:03:32 PM12/15/09
to comm...@googlegroups.com

If getters and setters (in any form, really) make it into an IE8 update that would be great news for library authors. But still, a significant proportion of user agents will continue to be IE6 or IE7 (or an unpatched IE8) for the foreseeable future. SecurableModules is a fundamental building block (as Wes is fond of pointing out) -- if this feature depended on a setter it would be unusable for truly common js.

Would it be too ugly to specify both (module.setExports and the module.exports setter that proxies to module.setExports) with the intent to eventually deprecate setExports? This would at least give library authors a choice between elegance and cross-browser availability.

Ash Berlin

unread,
Dec 15, 2009, 1:10:29 PM12/15/09
to comm...@googlegroups.com
On 15 Dec 2009, at 18:03, Dean Landolt wrote:

If getters and setters (in any form, really) make it into an IE8 update that would be great news for library authors. But still, a significant proportion of user agents will continue to be IE6 or IE7 (or an unpatched IE8) for the foreseeable future. SecurableModules is a fundamental building block (as Wes is fond of pointing out) -- if this feature depended on a setter it would be unusable for truly common js.

Would it be too ugly to specify both (module.setExports and the module.exports setter that proxies to module.setExports) with the intent to eventually deprecate setExports? This would at least give library authors a choice between elegance and cross-browser availability.

FWIW i think this is what I will implement in flusspferd.

Though of course all of this does make documenting modules slightly harder, as you have to somehow say 'this module just exports a class'. Just a mild annoyance, and worth it for the extra power it gives.

-ash

Jonathan Fine

unread,
Dec 15, 2009, 1:39:18 PM12/15/09
to comm...@googlegroups.com
On Mon, Dec 14, 2009 at 8:31 AM, Isaac Schlueter <isaacsc...@gmail.com> wrote:
While it's nice and predictable to return things on an exports object,
it leads to this pattern, which is somewhat ugly:

var Foo = require("foo").Foo;
var myFoo = new Foo; // whoa, that's a lotta foo!

Since modules so often define a single class, this would be much nicer:

var myFoo = new (require("foo"));

I can see that this could be done for JavaScript running on the server, but I don't see how it could be done in the browser.

I don't see how something like
   var fn = function(flag){
      if (flag)
           var Foo = require('foo');
      /etc
   }
could be made to work on the browser.

It seems to me (and I'm willing to be proved wrong) the only sensible way for modules to work on the browser is for each module to state up front the names of the modules it depends upon.  Such as in the dependencies field in
    http://github.com/280north/narwhal/raw/master/catalog.json

--
Jonathan

Ash Berlin

unread,
Dec 15, 2009, 1:44:32 PM12/15/09
to comm...@googlegroups.com
This is a side issue to how you export things from the module.

But yes, you either need to state up front, statically analyse to determine or to do a blocking XHR request, (or otherwise preregister a module with the browser loader).

-ash

Mark S. Miller

unread,
Dec 15, 2009, 1:58:00 PM12/15/09
to comm...@googlegroups.com
It seems to have bounced, for forwarding...

---------- Forwarded message ----------
From: Allen Wirfs-Brock <Allen.Wi...@microsoft.com>
Date: Tue, Dec 15, 2009 at 9:55 AM
Subject: RE: [CommonJS] require.setExports()
To: "Mark S. Miller" <eri...@google.com>, "comm...@googlegroups.com" <comm...@googlegroups.com>, "Pratap Lakshman (VJ#SDK)" <pra...@microsoft.com>


As part of its engagement in the drafting of ES5, Microsoft created a fairly complete ES5 implementation including support for accessor properties(ie, getters/setters).  However, that prototype is not the JavaScript implementation that ships as part of IE8.  The IE8 implementation only supports accessor properties on DOM objects.

 

Microsoft has disclosed that it is working on IE9, a successor to IE8 be we haven’t yet disclosed much about features or timelines.  Those details will presumably emerge in 2010. For now, you will have to make your own inferences from our public activities and engagements.

 

Allen Wirfs-Brock

 

From: Mark S. Miller [mailto:eri...@google.com]
Sent: Tuesday, December 15, 2009 9:42 AM
To: comm...@googlegroups.com; Allen Wirfs-Brock; Pratap Lakshman (VJ#SDK)


Subject: Re: [CommonJS] require.setExports()




--
   Cheers,
   --MarkM

James Burke

unread,
Dec 15, 2009, 2:40:44 PM12/15/09
to comm...@googlegroups.com
On Tue, Dec 15, 2009 at 9:03 AM, Dean Landolt <de...@deanlandolt.com> wrote:
> Isaac and Kris have a proposal up on the wiki so show some hands...
>
> http://wiki.commonjs.org/wiki/Modules/SetExports

I prefer not to saddle "module" with additional functionality. I feel
like the module variable in general is an over-complication, and in
the past used mostly for module.id == require.main, to signal a
main-like functionality. That could main-like functionality could be
implemented via a method convention for a main, depending on the
environment. That seems more robust, more declarative way to signal
"main" execution.

This setExports issue in particular is where I believe having the
module format with a function wrapper is more intuitive and more
fitting with JavaScript, particularly considering the module pattern:
http://yuiblog.com/blog/2007/06/12/module-pattern/

By being able to return an object for the definition of the "exported"
object, it avoids any extra special module or exports object.

It also has the benefit that the module format could be directly
usable in the browser, particularly if the module's dependencies are
specified outside the module wrapper.

The function wrapper also avoids the sync/async APIs and knowledges of promises.

So, more like this module format proposal:
http://wiki.commonjs.org/wiki/Modules/Transport/A

I use this for RunJS at the moment. For circular dependencies, I mixin
the properties of the returned object. I allow function returns, and
if a circular dependency, a proxy function is used.

So even if a format that uses a function wrapper is not desired by
this group, I still encourage not putting more things on "module" and
reconsidering "module" in general.

James

Kris Kowal

unread,
Dec 15, 2009, 2:51:05 PM12/15/09
to comm...@googlegroups.com
On Mon, Dec 14, 2009 at 11:50 AM, Kris Kowal <cowber...@gmail.com> wrote:
> Also, in an attempt to forestall a lengthy digression, I am +1 on the
> color of the bike shed.

I got a new idea for the color of the bike shed last night. One of
the reasons, as Kevin Mentioned, for the exports object is that it
resembles an idealized native syntax:

exports.foo = function () {
};

export foo = function () {
};

I was pondering whether and how this syntax should look if provided
natively. It occurred to me that we could add a non-assignment form
to the export keyword.

export {…};

So, to make the new setExports syntax resemble that, it occurred to me
that we could make the exports object a function. This means that
function prototype properties would be implicitly exported, although
overridable, but I do not foresee this being a problem. (In an
experiment a couple years ago, having the exports object be a Function
led to strange problems like assigning to name silently failing and
problems with the "include" form which depended on a "with" block.
The latter is not a concern for us. exports.name might break though).

I'm proposing instead of:

module.setExports({…});

Call the exports function:

exports({…});

Kris Kowal

Ash Berlin

unread,
Dec 15, 2009, 2:56:08 PM12/15/09
to comm...@googlegroups.com

On 15 Dec 2009, at 19:51, Kris Kowal wrote:

> I'm proposing instead of:
>
> module.setExports({…});
>
> Call the exports function:
>
> exports({…});
>
> Kris Kowal
>

Certainly an interesting idea. Is this directly the same as doing module.setExports({...}) ?

Also you can clear an objects prototype:

> x = function() {print("hi"); }
(function () {print("hi");})
> x.__proto__
function () {}
> x.__proto__ = {}
({})
> x()
hi

Works in spidermonkey - might be worth testing to see how it works in other engines?

Kris Zyp

unread,
Dec 15, 2009, 3:09:25 PM12/15/09
to comm...@googlegroups.com


Kris Kowal wrote:
> On Mon, Dec 14, 2009 at 11:50 AM, Kris Kowal <cowber...@gmail.com> wrote:
>
>> Also, in an attempt to forestall a lengthy digression, I am +1 on the
>> color of the bike shed.
>>
>
> I got a new idea for the color of the bike shed last night. One of
> the reasons, as Kevin Mentioned, for the exports object is that it
> resembles an idealized native syntax:
>
> exports.foo = function () {
> };
>
> export foo = function () {
> };
>
> I was pondering whether and how this syntax should look if provided
> natively. It occurred to me that we could add a non-assignment form
> to the export keyword.
>
> export {�};
>
> So, to make the new setExports syntax resemble that, it occurred to me
> that we could make the exports object a function. This means that
> function prototype properties would be implicitly exported, although
> overridable, but I do not foresee this being a problem. (In an
> experiment a couple years ago, having the exports object be a Function
> led to strange problems like assigning to name silently failing and
> problems with the "include" form which depended on a "with" block.
> The latter is not a concern for us. exports.name might break though).
>
> I'm proposing instead of:
>
> module.setExports({�});
>
> Call the exports function:
>
> exports({�});
>
+1

BTW, what does require("some-module-that-doesnt-replace-exports")() do?
Is the default just an empty function or a function that throws an
exception? (the silence is deadly principle might suggest the latter).

Kris

Kris Kowal

unread,
Dec 15, 2009, 3:16:52 PM12/15/09
to comm...@googlegroups.com
On Tue, Dec 15, 2009 at 12:09 PM, Kris Zyp <kri...@gmail.com> wrote:
> BTW, what does require("some-module-that-doesnt-replace-exports")() do?
> Is the default just an empty function or a function that throws an
> exception? (the silence is deadly principle might suggest the latter).

Oh. The exposition of the setExports function to anyone who requires
would actually be a big problem. However, since the mechanism require
exports() to throw an error any time it's used after it has been
required by another module, I think the case is covered. It throws,
whether it's called after a cyclic dependency makes the require, or if
it's called later.

Kris Kowal

Tom Robinson

unread,
Dec 15, 2009, 3:20:35 PM12/15/09
to comm...@googlegroups.com
Even in platforms without setters I think we could still produce a reasonable implementation. You won't be able to immediately throw an exception if an already require'd module is overridden, but you could check upon the next call to "require" or at the end of evaluating the module, no?

-tom

Irakli Gozalishvili

unread,
Dec 15, 2009, 5:10:08 PM12/15/09
to comm...@googlegroups.com
I really do like idea of having exports being a function rather then dictionary. The problem which I see though is bit ugly exports for classes. Of course there is a possibility of creating a local dictionary and then exporting it. Anyway I'm up for Kris's proposal of exports({...})

+1
 
--
Irakli Gozalishvili
Web: http://rfobic.wordpress.com/
Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands



Kris Kowal

Dean Landolt

unread,
Dec 15, 2009, 5:20:21 PM12/15/09
to comm...@googlegroups.com


It works around the issue of needing a module.exports setter nicely.

As long as implementers don't see any issues, +1

Charles Jolley

unread,
Dec 15, 2009, 5:43:22 PM12/15/09
to CommonJS
+1 on using module.exports = foo. This can be implemented easily even
without setters and it is better than having to generate yet another
function to the API. I've just implemented both in the SproutCore
loader and module.exports seems much cleaner.

Changing exports to a function is a really clever idea but given that
function has a special behavior on most JS engines I think it is not
advisable.

Also, one HUGE benefit of using the module.exports = foo approach
(IMO) is that I can extend another module API without having to copy
all the properties. This is great for perf.

For example:

// my_fancy_module.js:

var utils = require('utils', 'default');
var base = require('basic_module', 'anotherPackage');
exports = module.exports = utils.beget(base); // uses prototype
inheritence
exports.foo = 'blah'; // extend

now my_fancy_module exports the same API as basic_module with foo
added to it.

This is really nice for refactoring code.

-C

Kris Zyp

unread,
Dec 15, 2009, 6:16:09 PM12/15/09
to comm...@googlegroups.com


Charles Jolley wrote:
> +1 on using module.exports = foo. This can be implemented easily even
> without setters and it is better than having to generate yet another
> function to the API. I've just implemented both in the SproutCore
> loader and module.exports seems much cleaner.
>
> Changing exports to a function is a really clever idea but given that
> function has a special behavior on most JS engines I think it is not
> advisable.
>
The special behavior is that it is callable, right? Yeah, that's the idea.
> Also, one HUGE benefit of using the module.exports = foo approach
> (IMO) is that I can extend another module API without having to copy
> all the properties. This is great for perf.
>
> For example:
>
> // my_fancy_module.js:
>
> var utils = require('utils', 'default');
> var base = require('basic_module', 'anotherPackage');
> exports = module.exports = utils.beget(base); // uses prototype
> inheritence
> exports.foo = 'blah'; // extend
>
> now my_fancy_module exports the same API as basic_module with foo
> added to it.
>
> This is really nice for refactoring code.
>
Pretty easy to do with callable exports:

var utils = require('utils', 'default');
var base = require('basic_module', 'anotherPackage');
var subModule = utils.beget(base); // uses prototype inheritance
subModule.foo = 'blah'; // extend
exports(subModule);


Kris

Isaac Z. Schlueter

unread,
Dec 15, 2009, 9:08:55 PM12/15/09
to CommonJS

re: Callable "exports"

I kinda <3 having a dual-purpose callable exports. It's clever, and
pretty, and it tickles me to no end. It was just occurring to me
today, and I loled when I saw this.

OTOH, this leads to a few really odd situations.

For example:

exports.prototype ={ foo: "bar" }; // weird.
exports.name = "Hi, my name is!"; // fails silently, function.name is
read-only
new exports(); // huh?
exports.call(...) // I don't even wanna get into that one.
exports.caller = foo; // nice try.
f.arguments = "Lets have an argument!" // just kidding, it's still
null!

The issues go on and on.

No, I think, as cute as it is, if we're gonna treat something as a bag
of data, it should be a regular object, and if we're gonna treat it as
a callable, it should be a plain old function. Boring, I know, but
much safer.


re: module.id === require.main

Yeah, it might be nicer if there was a less crufty way to signal
"main" behavior. If the goal is to do away with the "module" var,
then maybe we should hang setExports() somewhere else, so that it's
not one more snag keeping "module" around.


re: module.exports setter

I'm +1 on that. It's fairly trivial to implement using the same logic
that's currently living in module.setExports, and if we are all going
to do it that way, I see no need for the extra function. Node
originally supported that syntax (albeit in a less-than-securable
way), and it was nice. I would have pitched that, but I thought we
were trying to stay away from ES5 behavior that isn't implementable in
ES3, no?


re: require.export(foo)

I do kind of like "export" as the function name, since it's a single
simple verb, and is even more close to that idealized syntax that Kris
was alluding to before:

es5: export { ... }
cjs: export({ ... })

However, it sort of implies to me that you can use that function to
export multiple items. IE:

export(foo);
export(bar); // now foo and bar are both exported? surprise! foo is
lost!

Otoh, a function with "set" in the name implies that it's just one
thing, and when you set it, and you lose the previous setting.

Also, I think "require.export" is just a bit weird. I'm thinking more
and more that require.setExports is also odd. The require function
should be about pulling stuff IN, not pushing stuff OUT.


--i

Wes Garland

unread,
Dec 18, 2009, 1:57:56 PM12/18/09
to comm...@googlegroups.com
*banging head until desk covered in blood*

So, we're throwing out require. Anything else you guys feel like changing?

I have to say, every time base system specifications start changing, I have to wonder why I'm bothering to try to align myself to any standard, because about the only thing that seems to be guaranteed about them is that they will change, and that backwards compatibility appears to be of little interest to this group.

Wes

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

Dean Landolt

unread,
Dec 18, 2009, 2:05:17 PM12/18/09
to comm...@googlegroups.com
On Fri, Dec 18, 2009 at 1:57 PM, Wes Garland <w...@page.ca> wrote:
*banging head until desk covered in blood*

So, we're throwing out require. Anything else you guys feel like changing?

I have to say, every time base system specifications start changing, I have to wonder why I'm bothering to try to align myself to any standard, because about the only thing that seems to be guaranteed about them is that they will change, and that backwards compatibility appears to be of little interest to this group.


How does this break backward compatibility? It's an extension to require that if ratified adds a pretty heavily-demanded feature in (what I believe to be) a backward compatible fashion. This doesn't break require, it extends it.

Wes Garland

unread,
Dec 18, 2009, 2:17:31 PM12/18/09
to comm...@googlegroups.com
> This doesn't break require, it extends it.

If that's true, then I jumped the gun and I apologize.  Too much stuff to catch up after a week's absence making me read too quickly.

That said, this extension definitely has implementation impacts, although perhaps those impacts could be limited to JavaScript-only modules in my environment -- it is not realistically compatible with blended-language modules.

The original proposal for modules was quite clear that the exports object was provided by the outside environment, and not returned from the module. This has led to specific implementation decisions which would need to be undone in order to support this extension.

It concerns me that we are allowing module code to affect the caller's environment in a non-obvious way.  What concerns me most, however, is that we appear to be adding features and evolving base system capabilities in an undirected manner.  I don't mind a little bit of careful module crafting in corner-cases -- I DO mind community fragmentation.

I believe that it is really, really important to build strong, foundational building blocks and to layer CommonJS on top of them, piece by piece.  Modifying the shape of those blocks is something that should be considered very carefully.

Require is the basic unit of construction in CommonJS -- even extending it needs to be carefully considered, because modules which make use of the extensions can't necessarily be loaded, and if they can be, the means to access them becomes unknown. This is the very definition of community fragmentation.

ihab...@gmail.com

unread,
Dec 18, 2009, 2:23:08 PM12/18/09
to comm...@googlegroups.com
On Fri, Dec 18, 2009 at 11:17 AM, Wes Garland <w...@page.ca> wrote:
> The original proposal for modules was quite clear that the exports object
> was provided by the outside environment, and not returned from the module.

For what it's worth, I agree with Wes. This behavior of the exports
object was added specifically to deal gracefully with cycles of
require(). Now setExports() will have to detect and throw on cycles,
or risk truly weird behavior of unrelated modules that are not doing
setExports().

Ihab

--
Ihab A.B. Awad, Palo Alto, CA

Kris Zyp

unread,
Dec 18, 2009, 2:29:29 PM12/18/09
to comm...@googlegroups.com

On 12/18/2009 12:23 PM, ihab...@gmail.com wrote:
> agree with Wes. This behavior of the exports
> object was added specifically to deal gracefully with cycles of
> require(). Now setExports() will have to detect and throw on cycles,
> or risk truly weird behavior

Yes, I think we agreed that setExports would throw if a require to that
module had already returned an exports object. In the module systems I
have worked with, this should be simple to implement, is very
deterministic, and is well worth it for the enormous benefits, IMO.
Kris

Dean Landolt

unread,
Dec 18, 2009, 2:35:31 PM12/18/09
to comm...@googlegroups.com


I completely agree with this sentiment, and yes, it could create fragmentation. But given that this is the one big issue many folks seem to have with require, if we can solve this in a manner that all current implementers are satisfied with and can implement, we can skip fragmentation. At least with the setExports proposal, all current modules will continue to work as expected. I kind of fancy Kris K's exports-as-a-function proposal but this would not be backward compatible so would probably not fly.

But I hear you the small pieces comment -- if all implementers can agree on a way to close this one issue have it implemented across the board we should probably all leave require be from then on.

Kevin Dangoor

unread,
Dec 18, 2009, 2:44:59 PM12/18/09
to comm...@googlegroups.com
On Fri, Dec 18, 2009 at 2:17 PM, Wes Garland <w...@page.ca> wrote:
Require is the basic unit of construction in CommonJS -- even extending it needs to be carefully considered, because modules which make use of the extensions can't necessarily be loaded, and if they can be, the means to access them becomes unknown. This is the very definition of community fragmentation.

Yes, you are absolutely correct about this. If this extension is truly difficult for anyone to implement in their system, that would be good to know. But, it sounds like implementing this is straightforward and adds an oft-requested feature.

This is not breaking backwards compatibility. It just breaks forward compatibility, which shouldn't be a big deal if it's easy to implement.

Kevin

--
Kevin Dangoor

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

Kris Kowal

unread,
Dec 18, 2009, 2:59:28 PM12/18/09
to comm...@googlegroups.com
Thanks to everyone has chimed in; this helps. I would like to remind
and encourage the point of view the that our "Show of Hands" tradition
is not voting and this isn't a democratic process, but a dialectic
process. There is a lot of support for module.setExports, but I do
not think that we should ratify it as a standard until all of the
engines are willing to implement it, and if that cannot be
accomplished, I think prototypes should remove the feature or rename
it to module.xMyEngineSetExports. I believe that this approach would
avoid the concerns about fragmentation and be the responsible
direction for discourse.

So, proponents of this feature should at this point focus on
convincing Mark, Ihab, and Wes. The arguments need to address both
how the feature adds value and the holistic implications for the
future, like what features would be complicated or precluded by the
system.

So far, we've heard that setExports would be more elegant in two cases:
* where a module exports a single constructor object, in which case
modules may strongly resemble constructors like Object that are both
instantiable and provide utility "class methods" like Object.freeze.
However, for this case, it does not enable new functionality.
* where the module's exports should dynamically proxy another
module's. For this case, it only enables new functionality in cyclic
dependencies and performs marginally better since there's no copy
operation on the other module's exports.

We've demonstrated in our prototypes that it is a less-than-10-line
fix in a pure-JavaScript module loader. We've also provided the
necessary caveat to guarantee referential identity of the return value
of require(), by precluding the possibility of using this feature in a
dependency cycle.

However, this feature does introduce complexity and could make
future-compatibility a burden and might not make it through the gate
of standardization at higher levels. I would even venture that this
is highly unlikely since ECMA holds the bar higher (in some sense, but
lower in the limbo sense). TC39 is generally reticent about
inelegance and unnecessary complexity. And we do need to be clear
that in the story of compelling ECMA to use this feature, it is one of
convenience and not necessity, except tenuously in the module proxy
case which might be explained in greater detail.

I've also been convinced that exports(object) would be a mistake
because of Function name collisions in the cases where it is never
called.

I have also withdrawn my hand either way.

Kris Kowal

Wes Garland

unread,
Dec 18, 2009, 3:09:15 PM12/18/09
to comm...@googlegroups.com
> If this extension is truly difficult for anyone to implement in their system, that would be good to know.
>  But, it sounds like implementing this is straightforward and adds an oft-requested feature.

In fact, implementing this in GPSEE is difficult.  Not impossible, but difficult.

The technical reasons are two-fold:
 - module exports are not plain JavaScript objects, but rather objects with a specific finalizer.  This finalizer in turn provides cues which let us know when it is safe to unload modules (if exports are final and GC has run, then the module code can be unloaded)
 - The module exports are fed to JavaScript after the native portion of a blended module run; if you replace the exports object, you lose the native properties/methods

What those two cases have in common is that we have previously specified the behaviour of the properties of the object returned by require(); we are now specifying the behaviour of the object itself.    The assumption that the object is an uninteresting {} object is an assumption of underlying implementation detail.

There are other cases to reason about, as well. I'll try and come up with some more thoughts soon, but my instincts are nagging me in a couple of directions:
 - How does replacing the exports object look compared to harmony proposals?  Right now, exports is essentially a named var object which has good co-relation with the export proposal.
 - Does current-require have any specific memoization timing? Is it affected by the cycle detector?  ISTR finding that memoization timing was critical in my require-current implementation to avoid cycles, but I forget the details now.
 - Is detecting cycles truly as simple as "am I yet un-memoized?"

Incidentally, I'm still trying to find a use-case for this proposal which isn't purely sugar or a method to repair an otherwise bad program design. Module proxying comes close, but I believe a single layer of indirection is enough to implement that with current-require.

Charles Jolley

unread,
Dec 18, 2009, 3:09:49 PM12/18/09
to comm...@googlegroups.com
FWIW - I added support for this feature in SproutCore/Tiki in about 15 minutes.  It's would be equally easy to implement in the other platforms I am familiar with (Narhwal & node.js).  I do think this could be specified as an optional feature for blended language loaders.

-Charles

Ash Berlin

unread,
Dec 18, 2009, 3:20:48 PM12/18/09
to comm...@googlegroups.com

On 18 Dec 2009, at 20:09, Charles Jolley wrote:

> FWIW - I added support for this feature in SproutCore/Tiki in about 15 minutes. It's would be equally easy to implement in the other platforms I am familiar with (Narhwal & node.js). I do think this could be specified as an optional feature for blended language loaders.

Something this low level *cant* be optional. If it is you are segregating the module market.

-ash

mob

unread,
Dec 18, 2009, 4:57:43 PM12/18/09
to CommonJS
On Dec 14, 12:31 am, Isaac Schlueter <isaacschlue...@gmail.com> wrote:
> While it's nice and predictable to return things on an exports object,
> it leads to this pattern, which is somewhat ugly:
>
> var Foo = require("foo").Foo;
> var myFoo = new Foo; // whoa, that's a lotta foo!

Given the issues, perhaps just a good old convenience function can
improve the original problem:

var my = require("foo").create();

Does this help?

-mob

Charles Jolley

unread,
Dec 18, 2009, 5:11:31 PM12/18/09
to comm...@googlegroups.com
> Incidentally, I'm still trying to find a use-case for this proposal which isn't purely sugar or a method to repair an otherwise bad program design. Module proxying comes close, but I believe a single layer of indirection is enough to implement that with current-require.

Placing all of a library's classes into a single namespace and placing one class per file are both very common patterns used to keep code structured and well isolated. They are time tested patterns and surely not bad program design.

I think the primary purpose of this proposed extension (as well as the import discussion I brought up a week ago) is to help code written with these patterns transition more easily to CommonJS modules.

There are many millions of lines of JS already out there - much of it follows this structure already. Given how often this issue comes up - just in the last month or so since I joined this list - I think that should be a pretty clear indicator that CommonJS needs to address it somehow.

Personally I think an import model would be more flexible. It covers more usage patterns and it makes it the responsibility of the importer to adopt the added API rather than the exporter, which is usually better. On the other hand, it is much more difficult to implement at the CommonJS-engine level.

setExports()/module.exports is probably a good middle ground that will make it easy for most well structured code to transition while minimizing the amount of effort existing CommonJS impl must put in to supporting it.

Anyway, that is the value I see in a feature like this - to reduce the barrier of entry for existing code that needs to be converted.

-Charles

Kevin Dangoor

unread,
Dec 18, 2009, 9:57:41 PM12/18/09
to comm...@googlegroups.com

This does get back to Wes' point: this proposal is basically sugar. Even the thought of building up a single namespace (as is common in client-side JS libraries) can still be done:

// module

var LegacyNamespace = {...};

exports.Legacy = LegacyNamespace;

// consumer of module

var Legacy = require("legacy").Legacy;



It's not terrible. Fixing this is the very definition of sugar. I'm not anti-sugar, though. But, this is something that absolutely has to be common and not optional (in terms of what's written in the spec).

Wes' comments about the implementation difficulty in GPSEE are duly noted. Other than that, does having a require.setExports() actually eliminate any capabilities or cause any problems? I want to be sure that the total cost of such a change is accounted for and weighed against the benefits.

I haven't seen any other issues in this thread beyond implementation in GPSEE and cyclic dependencies (which others think are easily dealt with). Is that right?

Ondřej Žára

unread,
Dec 20, 2009, 11:51:33 AM12/20/09
to comm...@googlegroups.com
I haven't seen any other issues in this thread beyond implementation in GPSEE and cyclic dependencies (which others think are easily dealt with). Is that right?



If I was the one to make a decision here, I would vote AGAINST this feature.

My primary reasons:

1) KISS. I truly *hate* offering my users multiple ways to achieve the same goals; I also believe that this "pleomorphism" is one of the reasons many coders truly despise PHP with its several millions of functions/methods. I like to add sugar to my tee, but too much sugar and there is no tee remaining.

2) COMPLEXITY. I am not very sure on this one, but it seems to me that in order to get setExports to work, one has to provide a separate version of "require" (or "setExports") to every module being loaded. So far, v8cgi is happily using one - shared - require() and everything works perfectly.

3) SEMANTIC ISSUES. Inside a module code (or, more precisely, inside any CommonJS code), there exists a simple rule of thumb, describing interaction with other code:
  - use "exports" to influence what we provide to outer world,
  - use "require" to get some other stuff from other modules.

However, the "require.setExports" name looks like a very strange hybrid - it is used to manipulate stuff *I* created, but it is located in a "namespace" defined for importing *other* stuff.

4) ROBUSTNESS. So far, the following was always true: "require(anyThing).constructor === Object". In my opinion, this assumption is a good one: whatever is the module being loaded doing, we are always sure that the result of calling require() is a normal object. We can rely on this because it is *not* the module who creates the exports; it is the CommonJS environment's task to properly handle require() and the "exports" object.
If we allow .setExports(), the statement above no longer holds. There is nothing known about the returned value from require(). Maybe this opinion is very old-school, but I strongly prefer determinism here and I find very convenient when functions return always the same data type, even if the language is flexible enough to act differently.



Ondrej


 

--

Isaac Z. Schlueter

unread,
Dec 20, 2009, 7:58:14 PM12/20/09
to CommonJS

On Dec 20, 8:51 am, Ondřej Žára <ondrej.z...@gmail.com> wrote:
> 1) KISS. I truly *hate* offering my users multiple ways to achieve the same
> goals;

I share your feelings about pleomorphism.

That being said, this feature would in fact add functionality that
does not currently exist. In the case that I have in mind for it, I'd
like to have one module proxy another, and my program's design can be
significantly simpler with this feature.

I'm not terribly enthused about ending up with a meaningless exports
object. Maybe (while it wouldn't work sans-ES5) it would be a good
idea to seal/freeze the original exports object if setExports is
called, so that any subsequent additions will fail? Alternatively, we
could get rid of the "exports" free var, and just use module.exports
exclusively. I wouldn't be happy about the added verbosity, but it
would cut down the API exposure.


> 2) COMPLEXITY. I am not very sure on this one, but it seems to me that in
> order to get setExports to work, one has to provide a separate version of
> "require" (or "setExports") to every module being loaded. So far, v8cgi is
> happily using one - shared - require() and everything works perfectly.

You don't actually need to provide a separate require function
(although nodejs and narwhal both do this anyway, and it seems a good
idea, since you otherwise have a vector for pollution between modules,
especially if I were to have a module that does "delete require.async"
or something weird like that.)

But, nevertheless, all you need in order to implement this function
is:
1. knowledge about which module is currently loading
2. knowledge about whether or not its been loaded already and returned
to some other caller.
3. a way to swap out the reference to the exports object with some
other thing provided by the JS.

As Wes pointed out, #3 is tricky in GPSEE. In CommonJS loaders that
implement the bulk of the module loading framework in JS, and
implement some sort of memoization when the module has finished
loading, it's pretty easy.


> 3) SEMANTIC ISSUES.


>   - use "exports" to influence what we provide to outer world,
>   - use "require" to get some other stuff from other modules.
> However, the "require.setExports" name looks like a very strange hybrid

Agreed. It's currently "module.setExports" (with some people arguing
for a setter on module.exports that does the same thing). Since we
already have module.id and module.uri, it seems like a more semantic
fit.

But, imo, if we're debating where to put it, we've already gotten past
the real objections. I'm ok with putting this thing wherever, and
long years working with web browsers have made me completely immune to
dealing with the ugliest APIs imaginable; as long as I get the
functionality I want, I'm happy.

> 4) ROBUSTNESS. So far, the following was always true:
> "require(anyThing).constructor === Object". In my opinion, this assumption
> is a good one: whatever is the module being loaded doing, we are always sure
> that the result of calling require() is a normal object.

How does that make the system more robust? It seems to me to be a
purely stylistic concern (and it's fine if you want to advocate for
that as a best practice and do it that way in your programs) but not
one that affects the inherent stability or feasibility of the system.

--i

Mark S. Miller

unread,
Dec 20, 2009, 9:18:37 PM12/20/09
to comm...@googlegroups.com
On Sun, Dec 20, 2009 at 8:51 AM, Ondřej Žára <ondre...@gmail.com> wrote:

I haven't seen any other issues in this thread beyond implementation in GPSEE and cyclic dependencies (which others think are easily dealt with). Is that right?



If I was the one to make a decision here, I would vote AGAINST this feature.

My primary reasons:

1) KISS. I truly *hate* offering my users multiple ways to achieve the same goals; I also believe that this "pleomorphism" is one of the reasons many coders truly despise PHP with its several millions of functions/methods. I like to add sugar to my tee, but too much sugar and there is no tee remaining.

+1. The original modules proposal, with only a simple "require" and "exports", had a pleasing minimality and taste. With the addition of "module" and now "setExports", this is being rapidly lost.

 
2) COMPLEXITY. I am not very sure on this one, but it seems to me that in order to get setExports to work, one has to provide a separate version of "require" (or "setExports") to every module being loaded. So far, v8cgi is happily using one - shared - require() and everything works perfectly.

3) SEMANTIC ISSUES. Inside a module code (or, more precisely, inside any CommonJS code), there exists a simple rule of thumb, describing interaction with other code:
  - use "exports" to influence what we provide to outer world,
  - use "require" to get some other stuff from other modules.

However, the "require.setExports" name looks like a very strange hybrid - it is used to manipulate stuff *I* created, but it is located in a "namespace" defined for importing *other* stuff.

4) ROBUSTNESS. So far, the following was always true: "require(anyThing).constructor === Object". In my opinion, this assumption is a good one: whatever is the module being loaded doing, we are always sure that the result of calling require() is a normal object. We can rely on this because it is *not* the module who creates the exports; it is the CommonJS environment's task to properly handle require() and the "exports" object.
If we allow .setExports(), the statement above no longer holds. There is nothing known about the returned value from require(). Maybe this opinion is very old-school, but I strongly prefer determinism here and I find very convenient when functions return always the same data type, even if the language is flexible enough to act differently.



Ondrej


 
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.

--

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.



--
   Cheers,
   --MarkM

Daniel Friesen

unread,
Dec 21, 2009, 6:32:50 AM12/21/09
to comm...@googlegroups.com
+1, even after Isaacs' comments.
When we first came up with require(); I had a strong dislike of it. I
equated it to a "bastard child of a script file and package system, much
like php's array()". I've warmed up to require and the module system
(though still wish we had a mode that worked like the browser, just
running a bunch of scripts in the global scope), I even dropped my own
package system plans and started to implement require();
setExports just makes me lose how I've warmed up to require() and makes
CommonJS an uncomfortable platform for me again.
I've been ignoring this thread for that very reason, Ondrej just
explained what I couldn't come up with a reason for.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Wes Garland

unread,
Dec 21, 2009, 3:53:23 PM12/21/09
to comm...@googlegroups.com
Hi, Ondrej, MarkM, Isaacs;

Thanks for chiming in!

MarkM:  I too like the simple "area" exposed by require/exports; I have, in fact, not exposed module yet -- I have not needed in any of my real-world programming tasks.  The ONE place I have found it to be absolutely necessary / non-sugar is implementing toSource --

exports.myClass.toSource = function() {
  return '(require("' + module.id + "').myClass(' + details + '))';
}

IsaacS: The case where you want a module to proxy another is an interesting application, however I am not totally convinced it is not mostly-sugar; an extra property on your "proxying" module could provide the extra level of indirection required on the object graph.  Of course, that would have to be done at the design phase.... another option, though, is replicating enumerable exports.

*hmm* - It occurs to me that ES-next will allow module proxying *without* setExports or enumerable property replication, through the use of the catch-all (undefined property hook).  FWIW, this feature is available today in Rhino and SpiderMonkey.

I, too, believe pleomorphism is the work of the devil. (Great word choice, BTW!) I have often said that I find PHP to be like programming in every language ever written -- at once. I am not a fan.

Ondras has a good point with respect to semantic consistency.  I believe Ihab can speak more clearly than I on this issue, but what it boils down to is this -- if the calling environment controls the exports, you have a good idea what require will return without analyzing the module.  If the module controls the exports, you are inverting the control ownership and allowing arbitrary modules to modify the umbrella environment.   The original specification required that the exports are an object. setExports() allows it to be any arbitrary type -- object, function, string, number... whatever.  I can reasonably see this causing problems with static analysis.

Here's an observation also worth making: if you don't know the return type of require(), I suspect you cannot reasoanbly write a monkey-thunking shim.  I think monkey-thunks have the likelihood of being important lazy-load techniques when creating namespace hiearchies in large CommonJS programming environments... I see observing the ability to monkey-thunk in a namespace/module system as something similar to observing Tennent Correspondence in a language design.

I admit I'm not the best guy on this list w.r.t. expressing formal reasoning about programming languages.. But I have been programming for *long* time  (not as long as MarkM, though -- I was on a 2KB machine writing in BASIC when he was at PARC!)  -- and over the course of many years and full-lifecycle projects, I have become increasingly trusting of my gut and really make people push hard when requesting core-level features in this type of environment.   KISS is very important: correctness wins every day of the week over sugar.

James Burke

unread,
Dec 21, 2009, 5:35:04 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 12:53 PM, Wes Garland <w...@page.ca> wrote:
> I, too, believe pleomorphism is the work of the devil. (Great word choice,
> BTW!) I have often said that I find PHP to be like programming in every
> language ever written -- at once. I am not a fan.

Pleomorphism also concerns me, but in a larger scope -- as it is
today, CommonJS modules cannot be run in the browser without
compromising some core aspect of browser-based JavaScript.

XHR-based loaders either use eval (not allowed in some environments
like Adobe AIR and hard to debug in some browsers), or use a script
tag with the XHR-retrieved source as the body of the tag (make
debugging hard since line numbers are messed up). A non-XHR loader,
one that is based on script src="" need function wrappers around the
modules. Currently there is no CommonJS standard for that function
wrapper, but even if there was, it would mean two flavors of CommonJS
modules.

It would be simpler to just have one flavor, and one that worked well
in the browser. That implies the flavor using a function wrapper for
the code that defines the module, and use the return value of that
function as the "exported" module. That approach removes the need for
a magic "exports" variable and an exports() or require.setExports
variants. Less module API surface.

I agree that allowing any return from the function wrapper complicates
things, but allowing either an Object or a Function seems to be a
reasonable restriction on the return value. Functions seem fairly
basic to JavaScript and the concept of modular programming.
Particularly constructor functions.

If module dependencies are passed to the module function wrapper, it
avoids the concern about needing a require and a require.async, more
module API surface reduction. It also makes it possible for the
modules to work naturally in the browser environment.

I appreciate this is a change from the existing module spec, but I
believe it reduces the module API surface, allows for functions as
module exports and it will gain much more traction since it will be
usable in the browser without needing transforms.

There is a proposal here for that kind of spec:
http://wiki.commonjs.org/wiki/Modules/Transport/A

along with an implementation:
http://code.google.com/p/runjs/wiki/RunJs

The implementation has more features than the Transport/A spec, so
feel free to ignore those parts of the implementation. I am not
assuming they would be in any official CommonJS module format.

I am sure there are some tweaks needed for the Transport/A spec,
probably some specifics around circular dependencies. Particularly
around Function modules: I prefer to allow them via a proxy function
in the circular dependency case, but if that causes serious identity
concerns that are more severe than limiting circular dependencies,
throwing an error in that case could work too.

James

Kris Kowal

unread,
Dec 21, 2009, 7:31:14 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 2:35 PM, James Burke <jrb...@gmail.com> wrote:
> There is a proposal here for that kind of spec:
> http://wiki.commonjs.org/wiki/Modules/Transport/A
>
> along with an implementation:
> http://code.google.com/p/runjs/wiki/RunJs

I clearly am not going to get the cycles to flesh this out as it needs
to be in the short term, but this is my counter-proposal. The purpose
is to provide the same functionality but also be an easy build target
for our present module format.

http://wiki.commonjs.org/wiki/Modules/Transport/B

I've left the unimportant bike-shedding decisions open.

Kris Kowal

Charles Jolley

unread,
Dec 21, 2009, 8:05:51 PM12/21/09
to comm...@googlegroups.com
Can someone explain to me why returning an Object from require() is so important? Most of the time you require('foo'), expecting it to return some api X. Why would it matter if that API is an Object, Function, or String?

If you require('foo') and get a String when you expected an Object that would cause an error, but in that case your problem is that 'foo' no longer exports the same API you expect. In that respect, it doesn't really matter that 'foo' did not return an Object but that it did not export the same API.

Also, all the -1's I've heard about this proposal seem to be mostly based on style preference. [GPSEE impl issues excepted] But I also don't see why this matters. setExports()/module.exports would be a feature used by a module implementor. If you don't like that style, don't use it in your modules.

Meanwhile, a request to change modules to make them more accommodating to 'namespaced' code comes through this list regularly afaict and this feature would address those concerns neatly.

It would significantly lower the barrier to entry for modules, meaning that even if you don't prefer to use this feature it will lead to you having more modules for you to use.

So, again, aside from impl issues, who loses by adding this feature?

-Charles

ihab...@gmail.com

unread,
Dec 21, 2009, 8:19:59 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 5:05 PM, Charles Jolley <cha...@sproutit.com> wrote:
> Also, all the -1's I've heard about this proposal seem to be mostly based on
> style preference.

My issue with this is that it re-introduces the problem of cyclic dependencies.

Historically, Kris Kowal's and my first thought about modules was that
they would construct *and return* the exported object. We quickly
realized that this caused problems when modules require() one another
in a cycle. The question was: what do we do?

* Have the module that does the require() causing a cycle get back 'undefined'?
* Throw an exception, meaning you can't have cycles?
* Go into an infinite loop?
* Do something more fancy? [see below]

Our solution, which was eventually adopted, was that the framework
would pass in the 'exports' object. In case of a cycle, the require()
which creates the cycle gets back a partially constructed 'exports'
object. This may cause *some* errors, but it creates a situation where
the module issuing that require() can reassess its needs. The author
of the require()-ing module can rearrange their dependency on the
require()-d object and, hopefully, fix the problem.

Now, with require.setExports(), we have a *second* way in which a
programmer would have to handle a cycle. If the module the require()
of which caused the cycle did a setExports(), the require()-er does
not get a partially constructed object but rather an exception. This
means they have to implement a *different* strategy -- they should
completely delay require()-ing of that module until after their own
module factory function returns.

It turns out that this solution -- delaying the cycle-creating
require() until after the require()-er's module factory returns -- is
precisely the "Do something more fancy" option we discussed. So if
we're going to have that, let's just go with having the module factory
return a new object (the semantics of setExports()) for everything,
and be done with it. That way, we would not have to deal with the
existence of Two Ways for Doing Things.

Ihab

James Burke

unread,
Dec 21, 2009, 8:54:08 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 5:19 PM, <ihab...@gmail.com> wrote:
> It turns out that this solution -- delaying the cycle-creating
> require() until after the require()-er's module factory returns -- is
> precisely the "Do something more fancy" option we discussed. So if
> we're going to have that, let's just go with having the module factory
> return a new object (the semantics of setExports()) for everything,
> and be done with it. That way, we would not have to deal with the
> existence of Two Ways for Doing Things.

So does always have "the module factory return a new object" mean that
explicit circular dependencies are not allowed (should throw an
error)?

What I do now:

I only allow generic objects and functions to be returned from the
module factory. If a function is going to be returned, that is
indicated outside the module factory.

A circular dependency happens. A requires B who requires A.

If A will be returning an object, then the loader creates a new object
to pass to B. Then B's return result from the module factory is given
to A. When A returns an object, the properties of A's object are mixed
in to the placeholder object that the loader created for A.

If A will be returning a function, then the loader creates a
placeholder function that will proxy to the real A's function. B uses
that proxy function for A, but other modules would get the real A
function.

I can appreciate this may be a problem for identity, if B passes its A
reference to some other module that also grabs the real A then A might
not equal A for that other module. This seems like a worse issue in
the Function return case, although it could still happen in the Object
case.

What I do not have is enough experience to know if this is really a
problem in practice. I am normally coding in the browser, in a loose
environment. Any sort of "is X the same as Y" normally means duck-type
property checks. Perhaps this issue really makes things hard for a
more secure environment?

If so, then it seems like having explicit circular dependencies is not
allowed if the module factory returns an object (a module would have
to do something later, via a nested load call perhaps, after first
returning its module definition from the module factory to simulate a
circular dependency).

James

Kris Kowal

unread,
Dec 21, 2009, 9:05:02 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 5:54 PM, James Burke <jrb...@gmail.com> wrote:
> So does always have "the module factory return a new object" mean that
> explicit circular dependencies are not allowed (should throw an
> error)?

We called this approach snapshotting, and here's a link to the
conversation about it:

http://groups.google.com/group/commonjs/browse_thread/thread/42ad9906daa8c11a/ab9f2b6e1fa108a3?lnk=gst&q=snapshotting#ab9f2b6e1fa108a3

One disadvantage is that the cyclic dependee gets a partial copy of
the API. The group strongly favored "late binding" and snapshotting
(my idea, for what it's worth) was rejected. I don't miss it; late
binding is handy.

Kris Kowal

Mark S. Miller

unread,
Dec 21, 2009, 9:09:49 PM12/21/09
to comm...@googlegroups.com
First, +100 for picking one way and sticking with it. The most common pathology of a standards body is to "solve" their inability to decide between alternatives by simply doing both. For better or worse, CoomonJS is now a functioning standards body, pathologies and all.

Next, +1 to just having sync require return a value of its choosing as its result. A sync require cycle would then cause an exception. An async require cycle would be fine, since an async requiring module is already expecting a promise for a result that only gets resolved eventually.

If we do this, a module could be identical to a class <https://mail.mozilla.org/pipermail/es-discuss/2009-March/009115.html> with the constraint that the class' only parameter is "require".

If we instead pick the current cycle friendly choice but kill setExports, that's still +99.

 
Ihab


--

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.





--
   Cheers,
   --MarkM

Charles Jolley

unread,
Dec 21, 2009, 9:15:41 PM12/21/09
to comm...@googlegroups.com

On Dec 21, 2009, at 5:19 PM, ihab...@gmail.com wrote:

> On Mon, Dec 21, 2009 at 5:05 PM, Charles Jolley <cha...@sproutit.com> wrote:
>> Also, all the -1's I've heard about this proposal seem to be mostly based on
>> style preference.
>
> My issue with this is that it re-introduces the problem of cyclic dependencies.

That is a reasonable concern and I agree the current exports approach is actually a very clean design. I think it would be a shame to give up this solution.

In my implementation of the setExports() feature, I actually used a slightly different technique that allows cyclical dependencies. Maybe this would offer a middle ground?

--

module.exports is a property which points to the exports hash. The module can change this property at will. Initially, it points to the same object that is defined on the exports global.

The first time a module's exports hash is returned, I save the current value of module.exports that in a hash. For any future require()'s of the same module, I check the value of module.exports against that saved value. If they have changed, I throw an exception.

You can see a link to this code here [see line 132 for the magic]:

http://github.com/sproutit/tiki/blob/working/lib/sandbox.js

--

The benefit of this approach is that it actually allows cyclical dependencies as long as you swap out the module.exports property BEFORE you introduce the cycle (or don't swap at all, which should be the most common case.) In other words, it only throws an exception when the cyclical dependency would actually cause a problem in your code. [Which is actually an improvement over the current API IMO.]


-Charles


ihab...@gmail.com

unread,
Dec 21, 2009, 10:02:15 PM12/21/09
to comm...@googlegroups.com
Hi Charles,

On Mon, Dec 21, 2009 at 6:15 PM, Charles Jolley <cha...@sproutit.com> wrote:
> In my implementation of the setExports() feature, I actually used a slightly
> different technique that allows cyclical dependencies.

That's pretty clever, thanks for sharing. For what it's worth, if
*all* modules *always* returned a new object (i.e., did not accept a
framework-supplied "exports"), the equivalent coping reaction would
be:

var result = {};
var initialized = false;

var initialize = function() {
if (initialized) { return; }
require('something'); require('otherthing'); ...
};

result.foo = function() {
initialize();
// Do work
};

result.bar = function() {
initialize();
// Do work
};

return result;

This is a great approach to use where one has access to change the
module that is doing the setExports() -- or where the author of that
module has done a good enough job of planning for that state of
affairs. If not, then other coping strategies have to be used.

Irakli Gozalishvili

unread,
Dec 21, 2009, 10:03:39 PM12/21/09
to comm...@googlegroups.com
As a alternative solution to a problem of proxy modules I would like to suggest ratifying some module which will help solving this particular problem.

require("exports").extend(exports, mymodule);

In most of the platforms it cane be easily implemented by

exports.__proto__ = myModule;

will make desired trick without copying any properties. Platforms that don't have an access to the __proto__ property still will be able to implement this by copying properties.

Obviously there can be some other tricks that particular platforms might apply to this.
--
Irakli Gozalishvili
Web: http://rfobic.wordpress.com/
Phone: +31 614 205275
Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands


Mark Miller

unread,
Dec 21, 2009, 10:16:50 PM12/21/09
to comm...@googlegroups.com
On Mon, Dec 21, 2009 at 7:03 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
As a alternative solution to a problem of proxy modules I would like to suggest ratifying some module which will help solving this particular problem.

require("exports").extend(exports, mymodule);

In most of the platforms it cane be easily implemented by

exports.__proto__ = myModule;

 
Mozilla plans to stop allowing __proto__ to be mutated. IE doesn't support __proto__ at all of course. __proto__ is not sanctioned by any standard.

 



--
Text by me above is hereby placed in the public domain

   Cheers,
   --MarkM

Charles Jolley

unread,
Dec 21, 2009, 10:22:59 PM12/21/09
to comm...@googlegroups.com
Incidentally, the implementation makes it very easy to "proxy" another module like so:

// poor-man's inheritance...
var K = function() {};
K.prototype = require('anotherModule'); // exports 'foo', 'bar'
exports = module.exports = new K();

// now you can write the rest of the module like normal...

require('someThirdModule'); // this may be cyclical...

exports.foo = function() { ... } ; // overrides foo

Ondřej Žára

unread,
Dec 22, 2009, 2:28:12 AM12/22/09
to comm...@googlegroups.com

Can someone explain to me why returning an Object from require() is so important?  Most of the time you require('foo'), expecting it to return some api X.  Why would it matter if that API is an Object, Function, or String?


I believe that this is a matter of some "common sense". Even if the language itself is not restricting me from doing so, I politely refuse to return variable data types in my functions.
I find it difficult to express exact motivations here, so I will instead show a code sample (very insane one):


function doSomething() {
  switch (arguments.length) {
    case 1:
      var arg = arguments[0];
      if (typeof(arg) == "string" || typeof(arg) == "number") { alert(arg); }
    break;
    case 2:
      var arg1 = arguments[0];
      var arg2 = arguments[1];
      if (typeof(arg1) == "number" && typeof(arg2) == "number") { return arg1+arg2; }
      if (typeof(arg1) == "object" && typeof(arg2) == "string { return arg1[arg2](); }
    break;
    ...
  }
}


With JS's dynamic typing, we are free to create such abominations. The function above is not related to module.setExports(), but I am using it to demonstrate that sometimes - IMHO - it is better to define some *rules* and obey them, instead of ending with a non-deterministic non-statically-analyzable stuff.

 

So, again, aside from impl issues, who loses by adding this feature?



I believe that ultimately, we all do. As we know, the border between a Bug and a Feature is sometimes very unclear; in this case, I stand on the buggy side :)


O.


 
-Charles

Irakli Gozalishvili

unread,
Dec 22, 2009, 3:30:38 AM12/22/09
to comm...@googlegroups.com
On Tue, Dec 22, 2009 at 04:22, Charles Jolley <cha...@sproutit.com> wrote:
Incidentally, the implementation makes it very easy to "proxy" another module like so:

// poor-man's inheritance...
var K = function() {};
K.prototype = require('anotherModule'); // exports 'foo', 'bar'
exports = module.exports = new K();

// now you can write the rest of the module like normal...

require('someThirdModule'); // this may be cyclical...

exports.foo = function() { ... } ; // overrides foo



In this case you don't mutate exports itself so ether you need setExports or module exports :)

@mark Yeap I know that's why I said that platforms not supporting it can implement their own shims or just copy properties

Anyway it's just an option and I don't know if I actually do like it :)

Daniel Friesen

unread,
Dec 22, 2009, 6:51:17 AM12/22/09
to comm...@googlegroups.com
Mark Miller wrote:
>
>
> On Mon, Dec 21, 2009 at 7:03 PM, Irakli Gozalishvili <rfo...@gmail.com
> <mailto:rfo...@gmail.com>> wrote:
>
> As a alternative solution to a problem of proxy modules I would
> like to suggest ratifying some module which will help solving this
> particular problem.
>
> require("exports").extend(exports, mymodule);
>
> In most of the platforms it cane be easily implemented by
>
> exports.__proto__ = myModule;
>
>
> Mozilla plans to stop allowing __proto__ to be mutated. IE doesn't
> support __proto__ at all of course. __proto__ is not sanctioned by any
> standard.
Additionally using __proto__ in some engines while using property
copying in others will introduce a inconsistency where in some engines
new properties will propagate, while in other engines they will not and
platform specific bugs will silently creep into programs. So I'd opt for
property copy ONLY, no __proto__ mangling.

>
> will make desired trick without copying any properties. Platforms
> that don't have an access to the __proto__ property still will be
> able to implement this by copying properties.
>
> Obviously there can be some other tricks that particular platforms
> might apply to this.
>
> --
> Irakli Gozalishvili
> Web: http://rfobic.wordpress.com/
> Phone: +31 614 205275
>
> Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands
> ...
>
>
> -Charles

>
>
> --
> Text by me above is hereby placed in the public domain
>
> Cheers,
> --MarkM

Wes Garland

unread,
Dec 22, 2009, 10:22:59 AM12/22/09
to comm...@googlegroups.com
James;

XHR-based loaders either use eval (not allowed in some environments
like Adobe AIR and hard to debug in some browsers), or use a script
tag with the XHR-retrieved source as the body of the tag (make
debugging hard since line numbers are messed up). A non-XHR loader,
one that is based on script src="" need function wrappers around the
modules. Currently there is no CommonJS standard for that function
wrapper, but even if there was, it would mean two flavors of CommonJS
modules.

Actually, it doesn't mean two flavours of CommonJS modules, nor affect the text of the module at all...It affects the module transport system and/or loader. *ALL* CommonJS implementations have to have either a function wrapper or something equivalent in order to load modules with a module-wide variable scope.. a function wrapper (closure) is, in fact, the only JavaScript construct which can allow this. Incidentally, while GPSEE uses engine-specific tricks for this, I know for a fact that Flusspferd is implemented with scripted closure wrappers.

So, for example, your module transporter might be a CGI program that looks like this (assuming you care nought for performance nor security) :

#! /bin/sh
#
# loads /var/www/js/libexec/module.js when query string is module
#
echo "Content-Type: application/x-javascript"
echo
echo "modules[${QUERY_STRING}] = (function () {"
cat /var/www/js/libexec/${QUERY_STRING}.js
echo "});"

and your require() implementation could be

var modules;
function require(moduleName) {

  return modules[moduleName];
}

Of course, that implementation of require requires either module preloading or a bit more code with some synchronous XHR.  Personally, I would use module preloading *and* tweak the loader to return more than one module:

#! /bin/sh
#
# loads /var/www/js/libexec/module.js when query string is module
# Multiple module name are separated by spaces
#
echo "Content-Type: application/x-javascript"
echo

emit_module()
{
  echo "modules[$1] = (function () {"
  cat /var/www/js/libexec/$1.js
  echo "});"
}

set -- `echo "${QUERY_STRING}" | sed 's/%20/ /'` ""

while [ "$1" ]
do
  emit_module "$1"
done
 
So, a script tag to preload multiple modules might look like

<script language="JavaScript" src="/cgi-bin/preload_modules.cgi?crypto mime bignum">
 
It would be simpler to just have one flavor, and one that worked well
in the browser. That implies the flavor using a function wrapper for
the code that defines the module, and use the return value of that
function as the "exported" module.

Frankly, that's an implementation detail. Modifying a global variable, returning a value.. heck, browser-local storage.. all valid options.

Something else to remember
 - CommonJS programs themselves are modules
 - It is not legal to use a return statement outside of a function
 - am not sure yet if "can programs return?" has been specified; it probably should be one way or another.

That approach removes the need for
a magic "exports" variable and an exports() or require.setExports
variants. Less module API surface.

I agree that allowing any return from the function wrapper complicates
things, but allowing either an Object or a Function seems to be a
reasonable restriction on the return value. Functions seem fairly
basic to JavaScript and the concept of modular programming.
Particularly constructor functions.


Keep in mind that APIs built around returning functions somehow seem to evolve by giving functions properties. Yucky yuck.  I prefer "pick one thing which does not restrict what you can do" - and to me that is an object. 

Important: Objects in JS in this context aren't just like C++ object instances.  They are more like namespaces.  I don't have a problem with a constructor in a namespace.

In fact, I bet most objections to require returning an object would go away if we called them namespaces, specified their behaviour, and let people implement as they wished. Of course, everybody would just use an object...

If module dependencies are passed to the module function wrapper, it
avoids the concern about needing a require and a require.async, more
module API surface reduction. It also makes it possible for the
modules to work naturally in the browser environment.

Module dependencies are not the business of the module user. They are the business of the module-writer.

Unfortunately, this leaks out at the browser module user unless
 - You can perform static analysis on the content/modules to work with your loader, or
 - You load modules synchronously, or
 - You use a completely different module loading scheme (async require)

Personally, I am in favour of the first choice for the browser environment. It is not a hard problem to solve in most cases.  I would be willing to help solve it, as well, with a loader/resolver written in CommonJS on the server end.

Wes

Note: As always, no code in this post has been tested, and is provided only for illustrative purposes.

Zachary Carter

unread,
Dec 22, 2009, 2:09:23 PM12/22/09
to comm...@googlegroups.com
Irakli created a prototype loader using that first technique[1].

[1]: http://github.com/Gozala/experiments/blob/experimental/commonjs/require.js
 

Wes

Note: As always, no code in this post has been tested, and is provided only for illustrative purposes.

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

--

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.



--
Zach Carter
http://zach.carter.name

Isaac Z. Schlueter

unread,
Dec 22, 2009, 3:56:23 PM12/22/09
to CommonJS
Lots of replies here, just gonna try to hit the bullet points as I see
them.

1. Cyclic Dependencies
Easily resolved, and afaict, a non-issue. Throw an error if setExports
() is called after a module's exports has been returned already to
another calling module. I've got this working in Node, and Kris has
it working in Narwhal.

2. But then we can't statically analyze our JavaScript because things
can return different things
That's already a problem all over the place in JavaScript, as Ondřej
pointed out. And yet we can build interesting programs with this
language, and are rarely troubled by this fact. Would JavaScript be
better *as a language* if this flexibility was not available? I'm not
convinced of that. Sure, it can be misused, but expressive
flexibility is why we're coding in JavaScript rather than C. If
anything, the fact that functions have the ability to return multiple
data types seems to indicate to me that modules should as well, since
they're analogous to function calls in so many other ways, but with
the added features of securability and (sometimes) lazy-loading.

3. API surface size
This is a real concern, but doesn't sound like a real objection to
me. If we're ready to debate the way to express this functionality,
I'm fine with that direction. But that's a separate issue, and we
probably ought not to bother with the "how" until we've reached some
level of agreement on the "should".

4. Implementation Difficulty
So far, no one has indicated that returning something other than a
loader-supplied object in a securable way is *impossible*. It ranges
from "kind of a pain, but doable" to "trivial". Seems like a non-
issue.

5. But __proto__ (or some other object-inheritance pattern) can
already do this
Well, not quite. For having one object map onto another, yes. It
would handle the module proxying case, albeit in a slightly more
klunky way. Adds an extra link to the prototype chain, and then
require("foo") !== require("foo-proxy"), increases the size of the
memoizer, etc. Not the end of the world, but not ideal. (And, if
it's so easy to circumvent, why not just enable the functionality in
an explicit and efficient way?)
For returning a class from a module, that would still not be possible,
since you can't transform an object into a callable. (That's not my
personal pet use-case, but it's a common request, as some of you have
mentioned.)

6. Argg! Web Browser!
Yeah. Web browsers are difficult for so very many reasons. Outside
the scope of this discussion, I think.

7. Monkey Thunking Shims
What exactly is a "monkey thunk"? Whatever it is, I so totally want
one. Is it too late to add it to my Amazon wishlist for xmas?

Anything I missed? Are there any other objections that don't stop at
"it smells funny" or "well, I'M sure never gonna use it in MY
programs"? I'm very interested in the reasoning behind Wes and Ondřej
and Mark's respective guts. But if it's just an objection to the way
the thing looks, perhaps we could make it less ugly. Let's say,
hypothetically, if you HAD to come up with a way to let a require()ed
module return something of its own choosing, is there any way
whatsoever that this could be done such that it actually makes the
exposed commonjs API better, or at least, not worse?

--i

Daniel Friesen

unread,
Dec 22, 2009, 4:57:19 PM12/22/09
to comm...@googlegroups.com
Isaac Z. Schlueter wrote:
> Lots of replies here, just gonna try to hit the bullet points as I see
> them.
>
> 1. Cyclic Dependencies
> Easily resolved, and afaict, a non-issue. Throw an error if setExports
> () is called after a module's exports has been returned already to
> another calling module. I've got this working in Node, and Kris has
> it working in Narwhal.
>
Someone else already made a post about how this now creates two separate
ways of handling cyclic deps. A cycle may return a partial object, or
may throw. Now to handle these cases you have to program ways of
handling both of them into your code, because you have no control over
which is going to happen since which issue result is going to happen is
completely up to the module you are not in control of.
(Not the body of my reply, I just threw this note in here)

> 2. But then we can't statically analyze our JavaScript because things
> can return different things
> That's already a problem all over the place in JavaScript, as Ondřej
> pointed out. And yet we can build interesting programs with this
> language, and are rarely troubled by this fact. Would JavaScript be
> better *as a language* if this flexibility was not available? I'm not
> convinced of that. Sure, it can be misused, but expressive
> flexibility is why we're coding in JavaScript rather than C. If
> anything, the fact that functions have the ability to return multiple
> data types seems to indicate to me that modules should as well, since
> they're analogous to function calls in so many other ways, but with
> the added features of securability and (sometimes) lazy-loading.
>
Functions are like modules? Since when?

Functions take arguments, modules don't.
Functions return data, modules don't. (Exporting is different than
returning. Exporting is closer to doing window.foo = myExpotedFunc in an
environment without modules than to returning)
Functions can be executed multiple times in different cases to do
different things. Modules are executed once and expected to do the same
thing every time.
Scripts are supposed to be executed once. Modules are supposed to be
executed once.
Functions have a `this` which will be a new object if the function is
called in a constructing way. Modules have no uniformly defined value
for `this` and cannot be called in a constructing way.

The only real similarity between functions and modules is that they both
are scoped so that var; does not escape the module or function. And that
similarity just makes most of us implement modules by wrapping them in
function data to trick the engine into scoping them the way we want.

Modules are closer to scripts than they are to functions. Heck, modules
are basically python script files in js (python scripts export similarly
to modules but export in an implied way).
> ...
>
> --i

Isaac Z. Schlueter

unread,
Dec 22, 2009, 10:30:37 PM12/22/09
to CommonJS
On Dec 22, 1:57 pm, Daniel Friesen <nadir.seen.f...@gmail.com> wrote:
> Isaac Z. Schlueter wrote:
> > 1. Cyclic Dependencies

> Someone else already made a post about how this now creates two separate
> ways of handling cyclic deps. A cycle may return a partial object, or
> may throw. Now to handle these cases you have to program ways of
> handling both of them into your code, because you have no control over
> which is going to happen since which issue result is going to happen is
> completely up to the module you are not in control of.

I think you might be misunderstanding when and why the throw occurs.

The setExports call will throw when setExports is called *after* a
module has been require()ed already. But this is an exceptional case,
and easily avoided. Cycles themselves will not necessarily throw.
This is demonstrable in the securable-setexports branch of my node
fork, and Kris Kowal has the same logic working in Narwhal.

This will throw:

a.js
exports.foo = function () { module.setExports("borked") };

b = require("a");
b.foo(); // throws! a's exports has already been returned can't be set
safely!

otoh, this is perfectly fine:

a.js
module.setExports("not borked");

b = require("a"); // b === "not borked"

--i

ihab...@gmail.com

unread,
Dec 22, 2009, 10:55:14 PM12/22/09
to comm...@googlegroups.com
Hi Isaac,

More typically and pathologically, this will throw:

A.js
require('B');
require.setExports({ ... });

B.js
require('A');

A first requires B. B requires A, thus making the export system give B
back the exports object of A. Now A tries to setExports(), but B has
already been given the exports.

Charles Jolley

unread,
Dec 23, 2009, 1:58:33 AM12/23/09
to comm...@googlegroups.com
But this is very easy to avoid:

A.js
var exp = {};
require.setExports(exp);

require('B');

exp.foo = ...
exp.bar = ...

B.js
require('A');

Ondřej Žára

unread,
Dec 23, 2009, 3:32:41 AM12/23/09
to comm...@googlegroups.com


2009/12/23 Charles Jolley <cha...@sproutit.com>

But this is very easy to avoid:

A.js
 var exp = {};
 require.setExports(exp);

 require('B');

 exp.foo = ...
 exp.bar = ...

 B.js
 require('A');



Well, you are basically ignoring what setExports() does, falling back to plain-good-old "I have an exports object, let's populate it" scenario...


O.


 

ihab...@gmail.com

unread,
Dec 23, 2009, 10:29:05 AM12/23/09
to comm...@googlegroups.com
On Wed, Dec 23, 2009 at 12:32 AM, Ondřej Žára <ondre...@gmail.com> wrote:
> Well, you are basically ignoring what setExports() does, falling back to
> plain-good-old "I have an exports object, let's populate it" scenario...

True, though he could arguably have made his *custom* exports special
in some way -- like having it inherit from some customized prototype
or something.

But another point is that this is precisely the kind of work a
setExports()-ing module would have to do to make sure it does not
create cycles. Which, if it chooses not to do so, or if that
refactoring is troublesome for some reason, could make it unusable in
many practical situations. Like for example, assuming a Doug Crockford
beget(), maybe the author of the module wants to do:

var foo = require('C').foo;
require.setExports(foo.begetObject());

Charles Jolley

unread,
Dec 23, 2009, 10:57:46 AM12/23/09
to comm...@googlegroups.com

> On Wed, Dec 23, 2009 at 12:32 AM, Ondřej Žára <ondre...@gmail.com> wrote:
>> Well, you are basically ignoring what setExports() does, falling back to
>> plain-good-old "I have an exports object, let's populate it" scenario...
>
> True, though he could arguably have made his *custom* exports special
> in some way -- like having it inherit from some customized prototype
> or something.

Right, like:

var Foo = function() {};
require.setExports(Foo);
require('blah'); // cycle's here

Foo.prototype.foo = ...
Foo.prototype.bar = ...

> But another point is that this is precisely the kind of work a
> setExports()-ing module would have to do to make sure it does not
> create cycles. Which, if it chooses not to do so, or if that
> refactoring is troublesome for some reason, could make it unusable in
> many practical situations. Like for example, assuming a Doug Crockford
> beget(), maybe the author of the module wants to do:
>
> var foo = require('C').foo;
> require.setExports(foo.begetObject());

This is true, but you have this same problem today even without setExports(). In fact I run into this all the time while converting SproutCore. For example:

--
module a.js:

var b = require('b');

exports.A = function() {};
A.prototype.bInstance = new b.B();

--
module b.js:

var a = require('a');

exports.B = function() {};
B.prototype.aInstance = new a.A();

--

What is really heinous about this problem is that which module will fail depends on which module get's require()'d by a third module first. And of course, no error will be thrown to warn me. When I run my code I'll just have these weird errors were a.A is not defined sometimes a b.B is not defined other times...

At least with setExports() the above scenario would be trapped and an exception thrown, making this sort of bug easy to track down and fix.

-Charles

Wes Garland

unread,
Dec 23, 2009, 11:03:59 AM12/23/09
to comm...@googlegroups.com
On Tue, Dec 22, 2009 at 3:56 PM, Isaac Z. Schlueter <i...@foohack.com> wrote:
2. But then we can't statically analyze our JavaScript because things
can return different things

That makes things difficult;  what's more difficult and a bigger deal is that it's difficult to reason about the behaviour of the require statement when it can modify the caller's environment.

The solution to that, of course, is to ditch the current require semantics entirely and use the return statement to return the exports object to the caller.  That has cyclic-dependency problems, though, which I don't think can be solved in a reasonable way.

If I understand the proposed cyclic dependency solution for setExports(), analysis also difficult to reason about whether a module can load or not because we now have hard-coded load requirements that could get affected by unrelated third-party modules loading another module unexpectedly.
 
Sure, it can be misused, but expressive
flexibility is why we're coding in JavaScript rather than C.  

Damned straight.
 
If
anything, the fact that functions have the ability to return multiple
data types seems to indicate to me that modules should as well, since
they're analogous to function calls in so many other ways, but with
the added features of securability and (sometimes) lazy-loading.

Right.  I'm either whole-hog on this point: either *only* objects  (my preference)... and if not, *anything goes*.  Restricting to, say, only function or object is silly.

5. But __proto__ ....
 
For returning a class from a module, that would still not be possible,
since you can't transform an object into a callable.  (That's not my
personal pet use-case, but it's a common request, as some of you have
mentioned.)

I personally think it's a use-case that's unnecessary sugar, and more likely to paint developers into corners in the long run.  It's completely reasonable to extend a module with "related stuff" or "stuff that describes it".  That's easy if what is returned is a namespace-like object. It's much less elegant if it returns a constructor.
 
6. Argg!  Web Browser!
Yeah.  Web browsers are difficult for so very many reasons.  Outside
the scope of this discussion, I think.

Nope.  We need to consider implementability on the web browser at every step.  That said, I don't see any real issues either way.
 
7. Monkey Thunking Shims
What exactly is a "monkey thunk"?  Whatever it is, I so totally want
one.  Is it too late to add it to my Amazon wishlist for xmas?

It is a term I coined on this list, which describes a thunking monkey patch; here is an example:

String.prototype.toByteString = function(charset) {
  String.prototype.toByteString = require("binary").String_toByteString;
  return String.prototype.toByteString(charset);
}

 
Anything I missed?  Are there any other objections that don't stop at
"it smells funny" or "well, I'M sure never gonna use it in MY
programs"?  I'm very interested in the reasoning behind Wes and Ondřej
and Mark's respective guts.  

My gut still tells me that there isn't a problem here that isn't strictly sugar, with the exception of a pure module proxy (which can be emulated with a catchall, or completely designed around).
 
But if it's just an objection to the way
the thing looks, perhaps we could make it less ugly.  Let's say,
hypothetically, if you HAD to come up with a way to let a require()ed
module return something of its own choosing, is there any way
whatsoever that this could be done such that it actually makes the
exposed commonjs API better, or at least, not worse?


Yeah. Using the return statement inside the module.  Unfortunately, there are technical problems with that approach that were discussed on this list nearly a year ago.

FWIW, I am not opposed on cosmetic grounds, just functionality.

Wes

ihab...@gmail.com

unread,
Dec 23, 2009, 11:06:46 AM12/23/09
to comm...@googlegroups.com
On Wed, Dec 23, 2009 at 7:57 AM, Charles Jolley <cha...@sproutit.com> wrote:
> At least with setExports() the above scenario would be trapped and an exception
> thrown, making this sort of bug easy to track down and fix.

Does this argue for setExports() semantics being the default / only
way to do things in CommonJS then?

Wes Garland

unread,
Dec 23, 2009, 11:10:41 AM12/23/09
to comm...@googlegroups.com
Now to handle these cases you have to program ways of
handling both of them into your code, because you have no control over
which is going to happen since which issue result is going to happen is
completely up to the module you are not in control of.

Daniel has made an interesting point here.

The current require interface says that require() only throws when something *fatal* happens, such as being unable to load a module from disk, or compile the code in the module.

The addition of a soft-throw semantic (load-order error) means that robust programs may be required to add try..catch blocks around require statements.

Wes

--

Wes Garland

unread,
Dec 23, 2009, 11:20:12 AM12/23/09
to comm...@googlegroups.com
Charles;

On Wed, Dec 23, 2009 at 1:58 AM, Charles Jolley <cha...@sproutit.com> wrote:
But this is very easy to avoid:

A.js
 var exp = {};
 require.setExports(exp);

This solution is essentially what happens right now: exports object is initialized upon module first-load.

In order for a module author to avoid a cycle when using require statements for third-party modules, he *must* use this pattern at the top of every module (well, before his first require statement, or call to any function which is not core JS and not part of the current module).

It strikes me that this winds up adding per-module boilerplate and room for error-making. That is not generally a good trade-off for sugar.

Wes

Wes Garland

unread,
Dec 23, 2009, 11:27:59 AM12/23/09
to comm...@googlegroups.com
On Wed, Dec 23, 2009 at 10:57 AM, Charles Jolley <cha...@sproutit.com> wrote:

What is really heinous about this problem is that which module will fail depends on which module get's require()'d by a third module first.  And of course, no error will be thrown to warn me.  When I run my code I'll just have these weird errors were a.A is not defined sometimes a b.B is not defined other times...

Would you mind constructing a complete example for this case?  I am having a hard time visualizing how that could happen with modules which merely assign to exports.  Do you have inline code in your modules which perform flow control during load?
 
At least with setExports() the above scenario would be trapped and an exception thrown, making this sort of bug easy to track down and fix.


This would count as a non-sugar use-case for setExports if the modules exhibiting Heisenbugs are in fact well-encapsulated, modular libraries (collections of functions) as opposed to meandering inline program flow or something.

Wes

Charles Jolley

unread,
Dec 23, 2009, 1:05:14 PM12/23/09
to comm...@googlegroups.com
Wes and Ihab,

I am not trying to argue that setExports() should be used all the time. In fact I think most new code should be written to use exports. setExports() is worth adding to support some important edge cases such as porting existing namespaced JS.

What I am trying to argue with these examples is that setExports() is not more fragile than exports with regard to the cyclical references.

-Charles

Irakli Gozalishvili

unread,
Dec 24, 2009, 6:08:48 PM12/24/09
to comm...@googlegroups.com
I do agree with a point that export of the Classes is just a sugar and honestly I don't see why is it so much better to write

var foo = new (require("foo"))();

instead of

var foo = new (require("foo").Foo)();

But I do agree that proxy modules are something really useful, so I just have another idea (guess it's horrible as well but... :)

What about something like

"foo"
exports.foo = function foo() {
    //whatever
}
// special property for proxy
exports[":"] = require("bar");

"bar"
exports.bar = function bar() {
    //whatever
}

require("foo").bar === require("bar").bar // true

module loader can do something like:

if (Object.prototype.hasOwnProperty.call(exports, ":")) {
    var proxy = exports[":"];
    delete exports[":"];
    for (var key in proxy) {
        if(!Object.prototype.hasOwnProperty.call(exports, key)) {
            exports[key] = proxy[key];
        }
    }
}


Obviously it's not the nicest solution but that's what we do anyway from the module itself. so I do think doing it in the require will reduce some boilerplate in the modules.

Regarding the setExports: My personal opinion here is that its better to stick with one way to exports things, either use exports as function to exports stuff (exports.foo = "bar" will not export undefined) or to an exports as we have it now.


--
Irakli Gozalishvili
Web: http://rfobic.wordpress.com/
Phone: +31 614 205275
Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands


Dean Landolt

unread,
Dec 25, 2009, 4:47:55 AM12/25/09
to comm...@googlegroups.com
On Thu, Dec 24, 2009 at 6:08 PM, Irakli Gozalishvili <rfo...@gmail.com> wrote:
I do agree with a point that export of the Classes is just a sugar and honestly I don't see why is it so much better to write

var foo = new (require("foo"))();

instead of

var foo = new (require("foo").Foo)();

But I do agree that proxy modules are something really useful, so I just have another idea (guess it's horrible as well but... :)

What about something like

"foo"
exports.foo = function foo() {
    //whatever
}
// special property for proxy
exports[":"] = require("bar");

"bar"
exports.bar = function bar() {
    //whatever
}

require("foo").bar === require("bar").bar // true

module loader can do something like:

if (Object.prototype.hasOwnProperty.call(exports, ":")) {
    var proxy = exports[":"];
    delete exports[":"];
    for (var key in proxy) {
        if(!Object.prototype.hasOwnProperty.call(exports, key)) {
            exports[key] = proxy[key];
        }
    }
}


Obviously it's not the nicest solution but that's what we do anyway from the module itself. so I do think doing it in the require will reduce some boilerplate in the modules.


I like it better than the __wunderbar__ alternatives but yeah, it's still a hack. The behavior would be far from obvious to new users and it may not be clear that once you hang a ":" key you clobber any other exports.

One thing everyone seems to agree on is that we'd need unanimous support from implementers. Sadly, at least for now, it looks like setExports, __init__, and friends aren't meant to be.

Isaac Z. Schlueter

unread,
Jan 2, 2010, 9:28:38 PM1/2/10
to CommonJS
On Dec 23 2009, 10:05 am, Charles Jolley <char...@sproutit.com> wrote:
> What I am trying to argue with these examples is that setExports() is not more fragile than exports with regard to the cyclical references.

I think Charles made a pretty strong case for this. module.setExports
is not any worse than what we've already got, in terms of cyclical
brittleness. In fact, it's more robust, since you'd throw an error in
cases that are silently problematic with the current system.

On Dec 23 2009, 8:10 am, Wes Garland <w...@page.ca> wrote:
> The addition of a soft-throw semantic (load-order error) means that robust
> programs may be required to add try..catch blocks around require statements.

To which, I say "meh". There will always be bad code out there, and
sometimes you'll have to decide between the cost of using something
unreliable vs the cost of writing it from scratch yourself. We've all
been there.

If you have a lot of cyclical dependencies, and you're using setExports
() in those cases, then your code is doing something it really
shouldn't; it's sloppy; and throwing an Error is probably the right
call. If you're using sloppy code, then you need to be aware that it
might not work very reliably. That's just life.

I don't think that we can solve that problem, and more importantly, I
think if we try to, we'll just reduce the flexibility of the overall
system. I'm not convinced that there are any non-pathological cases
where setExports fails and our existing exports object system doesn't
fail even worse.

On Thu, Dec 24, 2009 at 6:08 PM, Irakli Gozalishvili
<rfo...@gmail.com>wrote:

> // special property for proxy
> exports[":"] = require("bar");

Personally, I'd like to reduce magic (or, at least, not introduce any
*new* magic) wherever we can. An explicit function call is a message
asking the framework to please take some action. If the framework
knows that it can't safely take that action, then it throws an error,
which is a very firm and decisive "No" response. A magic property is
not nearly as clear, and can have unexpected side-effects.

A module proxy should return *exactly* the same object as the module
it's proxying, and it should work even if the proxied module does
something snazzy like calling exports.__defineSetter__ without
providing a __defineGetter__ for the property.

I'd very much oppose any kind of __wonderbar__ or magic property
solution at the CommonJS level. If higher-level frameworks want to
add magic, then that's their prerogative.

On Dec 25 2009, 1:47 am, Dean Landolt <d...@deanlandolt.com> wrote:
> One thing everyone seems to agree on is that we'd need unanimous support
> from implementers. Sadly, at least for now, it looks like setExports,
> __init__, and friends aren't meant to be.

I definitely don't think we all agree to that. ;P All of our
implementations have slight deviations from one another. setExports
functionality is actually already possible (and unsafe) in nodejs by
setting the module.exports object directly.

In 91 messages of discussion, there's been a lot of quibbling over the
shape of it (which is great, and we could probably use more), but I
haven't seen anything showing that setExports adds problems we don't
already have.

--i

Dean Landolt

unread,
Jan 3, 2010, 1:45:19 PM1/3/10
to comm...@googlegroups.com

I didn't mean it couldn't be implemented, just that it likely couldn't really be standardized unless it was likely that it would see universal implementation. I know it's already in node and I believe some variations on "magic" exports keys are in some implementations. Hell, some implementations even let you replace the exports object. But if your goal is a package that can be used across the various CommonJS universe the ability to count on some version of this feature seems far off at best. That doesn't mean we should give up on trying and stop talking about it -- perhaps I shouldn't have been so pessimistic.

Charles Jolley

unread,
Jan 3, 2010, 2:02:29 PM1/3/10
to comm...@googlegroups.com


I definitely don't think we all agree to that. ;P All of our
implementations have slight deviations from one another.  setExports
functionality is actually already possible (and unsafe) in nodejs by
setting the module.exports object directly.

I didn't mean it couldn't be implemented, just that it likely couldn't really be standardized unless it was likely that it would see universal implementation. I know it's already in node and I believe some variations on "magic" exports keys are in some implementations. Hell, some implementations even let you replace the exports object. But if your goal is a package that can be used across the various CommonJS universe the ability to count on some version of this feature seems far off at best. That doesn't mean we should give up on trying and stop talking about it -- perhaps I shouldn't have been so pessimistic.

FYI - tiki (sproutcore's CommonJS loader) has an implementation just like node.js  You change module.exports.  

-C
Reply all
Reply to author
Forward
0 new messages