exports.init = function () {
this.foo = 10;
};
exports.prototype.bar = function () {
return this.foo;
};
var Map = require('map');
var map = new Map({'a': 10});
This could easily be facilitated by making the exports object a
function that calls the module's init method on itself.
var exports = function () {
this.init.apply(this, arguments);
};
How do you guys feel about this extension to the module standard? We
would state that the "exports" object must be a function that applies
the context object's "init" method with the same arguments with which
it was called.
I don't get it. What does this accomplish?
I don't mind the initial suggestion, but I think it's early to declare
that "most modules define exactly one class/function", isn't it? If
our library ends up looking anything like Python's, there will
potentially be many modules that contain many functions...
Kevin
--
Kevin Dangoor
work: http://labs.mozilla.com/
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com
Yep, that makes sense. Sounds like a good solution to me to provide
convenience in the class-per-module case and still be nice in the
bag-o-stuff-in-a-module case as well.
Kevin
I see. In that case, I think it is a bad idea. ES-Harmony style
classes-as-sugar have no init method. They desugar to the Crock style
objects-as-closures pattern. Also, the classical style for using JS
prototype inheritance to simulate classes also has no init method.
Unlike either JS classic style or Crock style objects-as-closures,
Smalltalk has no other choice but init methods. This made attempts to
secure Smalltalk harder. As explained at footnote 4 on page 66 of
<http://erights.org/talks/thesis/>, to prevent one's clients from
re-initializing you, each object must carry an extra "am I
initialized?" flag, and each init method must repeat boilerplate to
test and set this flag. This is needless noise and error prone.
--
Cheers,
--MarkM
Mark S. Miller wrote:
> On Tue, Mar 24, 2009 at 8:56 AM, Kris Zyp <kri...@gmail.com> wrote:
>
>> It's the constructor method. It gets called when a new object is
>> instantiated by calling the function returned by the module. It's there
>> to make it viable to have the exports object (the object returned by
>> require) viable as a JS style class.
>> Kris
>>
>
> I see. In that case, I think it is a bad idea. ES-Harmony style
> classes-as-sugar have no init method. They desugar to the Crock style
> objects-as-closures pattern.
Why would the objects-as-closures pattern be incompatible with "init".
"init" can return an object.
(Also, that's just one of the proposals on the table, but from what I
have seen so for, I think ES-harmony should definitely go a different
route. However, that's another discussion. At the very least, it is
certainly not uniformly accepted to point of something we should design
around).
> Also, the classical style for using JS
> prototype inheritance to simulate classes also has no init method.
>
>
It has a constructor though. That's the role init is fulfilling, since
you can't assign a constructor to the exports variable (and have it be
returned).
> Unlike either JS classic style or Crock style objects-as-closures,
> Smalltalk has no other choice but init methods. This made attempts to
> secure Smalltalk harder. As explained at footnote 4 on page 66 of
> <http://erights.org/talks/thesis/>, to prevent one's clients from
> re-initializing you, each object must carry an extra "am I
> initialized?" flag, and each init method must repeat boilerplate to
> test and set this flag. This is needless noise and error prone.
>
>
I don't understand. How would an object be re-initialized? You can use
the new operator an existing instantiated object. Maybe I missing
something, could you give an example?
Kris
Hi Chris, perhaps I am missing something. Could you show how the std
Point class example would be expressed using an init method?
--
Cheers,
--MarkM
s/Chris/Kris/
--
Cheers,
--MarkM
> How do you guys feel about this extension to the module standard? We
> would state that the "exports" object must be a function that applies
> the context object's "init" method with the same arguments with which
> it was called.
Yes, please.
This is one of the few remaining issues I have with the module system.
This seems to solve it nicely.
Con:
Personally, I intend to use Chiron types, which resemble Python's
types (mixins, MRO, bottom-up construction, top-down initialization,
no "this", no "new", no "prototype"). Each layer can be frozen after
construction, so it's securable, simple, and extensible. That being
said, I have no intention of attempting to force them on anyone else.
I don't expect other people to /not/ force a type system on me, but
whatever type system we use is likely to disappoint me in at least a
dozen ways and I'd like to defer that discussion. (this is why I've
framed the File API discussion around the API and usage, not the
implementation.)
Wes Garland mentioned objections, despite being an early proponent of
this style, since he encountered difficulty implementing it in
SpiderMonkey.
Also, this design does have /some/ impact on people hosting exports as
a bundle of stuff. It causes the methods of Function to be reserved
for the exports object. There isn't even strict agreement about what
these members are among implementations, and some of them are
transparently read-only (they do not throw errors on write), like
Function.name in SpiderMonkey. These methods include "call" and
"apply", and sometimes "name", and "eval", to speak nothing of
extensions added by libraries.
Pro:
Dojo and Jack already code to the module-as-class pattern by assigning
exports to the name of the type they export. All methods are
effectively "static" methods of the class. Ruby establishes precedent
for this.
Against Wes's reservations, I agree with Kris Zyp that this proposal
is certainly implementable in C for non-JS module system implementors.
I think it might be better if "init" were a member of the prototype so
that it can be polymorphic, rather than a reserved name of the exports
object.
Mark, I believe the Point implementation might look like this:
exports.init = function (x, y) {
this.x = x;
this.y = y;
};
exports.prototype.getX = function () {
return this.x;
};
Kris Kowal
If I recall the point of Point, I think it would look like:
// Point.js
exports.init = function(x, y){
return {
getX : function(){
return x;
},
setX: function(newX){
x = newX;
},
...
};
}
and if I remember closures-as-classes approach to "subclassing"
correctly, I think we could do:
// ThreeDPoint.js
var Point = require("Point");
exports.init = function(x, y, z){
var point = new Point(x, y);
point.getZ = function(){
return z;
};
point.setZ = function(newZ){
z = newZ;
};
return point;
}
I (nor Kris) didn't explicitly state that init should follow the same
semantics as constructor functions, but that is necessary for these
examples to work. Hence, in js-implemented require function, exports
would be roughly defined:
var exports = function(){
return exports.init.apply(this,arguments) || this;
};
(I can't actually recall the exact rules of when a "new" call to a
constructor returns the newly created object and when it returns the
returned value, but I think it is the former when the return value is
falsy).
Kris
Kris Kowal wrote:
> [snip]
> Against Wes's reservations, I agree with Kris Zyp that this proposal
> is certainly implementable in C for non-JS module system implementors.
>
> I think it might be better if "init" were a member of the prototype so
> that it can be polymorphic, rather than a reserved name of the exports
> object.
>
I have no objections to this. In fact, that is what I do in Persevere,
Class.prototype.initialize is the method that I call on construction (so
I guess I would vote for that spelling).
My main objection to my proposal is that I can't declare classes with
declarative syntax. I wish I could write:
({
prototype:{
initialize: function(x, y){
this.x = x;
this.y = y;
},
getX : function(){
return this.x;
},
...
}
})
In Persevere, data binding is a central concept, so classes can be
updated in real-time without rebooting the server, and the changes are
directly persisted to the source files. Thus, if you do this at the console:
Point.prototype.getX = function(){ return this.x + 2};
This would actually result in the source file being updated with the
change. Of course Persevere needs some reasonably deterministic method
for serializing the state of the classes back to source files and this
only works well with declarative format, so a separate module system
with declarative syntax is the first-class module system for classes.
Classes that are generated through imperative means are absurdly
difficult to re-serialize in response to real-time (programmatic)
changes to that class. Consequently ServerJS modules are treated
somewhat as second-class modules in Persevere.
Kris