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]
[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.
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.
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"));
--
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.
On Dec 14, 7:21 am, "Mark S. Miller" <erig...@google.com> wrote:That is possible now. However, it doesn't solve the issue with
> Would
> var myFoo = new (require("foo").Foo)();
> solve your original problem with no new mechanism?
proxying one module's exports from another. And, it's not quite as
pretty :)
Also, what Kris said.
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
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
On Tue, Dec 15, 2009 at 7:25 AM, Ryan Dahl <coldre...@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
--
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.
<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).
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.
On Tue, Dec 15, 2009 at 9:07 AM, Kris Zyp <kri...@gmail.com> wrote:Dean Landolt wrote:I have long been in favor of being able to set the exports, so +1, but I
> Isaac and Kris have a proposal up on the wiki so show some hands...
>
> http://wiki.commonjs.org/wiki/Modules/SetExports
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.
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.
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"));
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)
Kris Kowal
*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.
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
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
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.
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
> 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
Given the issues, perhaps just a good old convenience function can
improve the original problem:
var my = require("foo").create();
Does this help?
-mob
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
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?
Kevin
--
Kevin Dangoor
work: http://labs.mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com
--
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
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
You received this message because you are subscribed to the Google Groups "CommonJS" group.
Kevin
--
Kevin Dangoor
work: http://labs.mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com
--
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.
~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]
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
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
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
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
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
We called this approach snapshotting, and here's a link to the
conversation about it:
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
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.
> 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
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.
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;
// 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
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?
So, again, aside from impl issues, who loses by adding this feature?
-Charles
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
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.
Wes
Note: As always, no code in this post has been tested, and is provided only for illustrative purposes.
--
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.
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
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
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
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.
A.js
var exp = {};
require.setExports(exp);
require('B');
exp.foo = ...
exp.bar = ...
B.js
require('A');
But this is very easy to avoid:
A.js
var exp = {};
require.setExports(exp);
require('B');
exp.foo = ...
exp.bar = ...
B.js
require('A');
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());
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
2. But then we can't statically analyze our JavaScript because things
can return different things
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.
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.)
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?
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.
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?
Does this argue for setExports() semantics being the default / only
way to do things in CommonJS then?
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.
But this is very easy to avoid:
A.js
var exp = {};
require.setExports(exp);
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.
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
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 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
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.