I'm in favor of a simpler load system that simply checks to see if a
file has been loaded previously, looks in search paths, and
essentially does a global eval or loads a binary lib (similar to
Ruby's "require" system)
I've discussed it a lot on IRC but I'll summarize my argument here.
1) The API:
This example was taken from another thread:
var greeten = require('greet-en').greet;
This is not user friendly at all. I've never encountered a module
system that requires you specify the name of the module, the name of
each object from the module you wish to import, and the name you want
to assign each object to.
The VAST majority of the time I just want to do:
require("File")
and have access to the File object.
Code should be properly namespaced to prevent collisions. We can
enforce it in the package management repository if we want.
2) Interoperability with the browser:
While it's possible to share code with the browser, you'll have to
write your module differently than the "normal" way, since there's no
way to automatically place all code from a file into a separate scope
in the browser, as is done on the server. That is, on the server you'd
write:
json_parse = function() { ... }
and it's automatically part of it's own module, whereas for the
browser you'd write the following in order to prevent global pollution:
JSON = {};
JSON.json_parse = function() { ... }
On the server, the first would be imported like:
var json_decode = require("json").json_decode;
While the second would have a useless extra level of namespacing:
var json_decode = require("json").JSON.json_decode;
I fear that half of the libraries will be written in the first style,
while half will be written in the second style. I realize you usually
have to look at API documentation anyway, but I'm a fan of
consistency, and this does not encourage consistency.
3) Interoperability with other module systems:
There's already a convention in JavaScript to namespace "modules"
simply using JavaScript objects. A global eval load system supports
that well.
I suspect many libraries that have their own module system (Dojo?
YUI?) would be incompatible with anything as complicated as the
proposed system.
And, as noted in the other thread, people are working on adding a
module system to the official ECMA spec. It would be a shame if we
came up with our own system that was incompatible with the official
one (if it's ever actually added to the spec).
4) Implementation difficulty:
Are we sure we can implement the proposed system in all the major
interpreters without too much trouble? ondras mentioned in IRC
yesterday that V8 doesn't support executing code with arbitrary
scopes, as is done in the example implementation on the Helma site .
There may be a workaround, I don't know.
A global eval load system can be implemented in pure JavaScript (given
a standard File API) and can be shared across all the interpreters. It
could even be used in the browser if you replace/abstract File with
XMLHttpRequest.
I'm interested in hearing if others agree, and if you don't, why not.
Thanks,
-Tom
> I'm in favor of a simpler load system that simply checks to see if a
> file has been loaded previously, looks in search paths, and
> essentially does a global eval or loads a binary lib (similar to
> Ruby's "require" system)
I'm not against something like an include() function which operated
the same was as <script src=""> (ie, same as the C preprocessor's
#include statement). Over time, I expect people wouldn't really use
this much, at least for libraries, but it would certainly be useful
for existing bodies of JS code being used in browsers today.
> 1) The API:
>
> This example was taken from another thread:
>
> var greeten = require('greet-en').greet;
>
> This is not user friendly at all. I've never encountered a module
> system that requires you specify the name of the module, the name of
> each object from the module you wish to import, and the name you want
> to assign each object to.
>
> The VAST majority of the time I just want to do:
>
> require("File")
>
> and have access to the File object.
The code example was slanted for brevity. Here's how I would have
written the code "in real life".
var greet_lib = require('greet-en')
greet_lib.greet("hello")
greet_lib.greet("world")
I'd do it this way, because presumably these libraries contain more
than one function, so you don't really want to assign a local for each
function, but one per module, then dereference the function from the
module object. This is similar to common usage in Python (though
Python allows other behaviors also).
> 2) Interoperability with the browser:
I think we can get pretty close, actually. In the playing I've done,
you have to prefix variables in the module with var to prevent
infecting the global scope, but beyond that, I think you can do what
you need. Presumably we'll get this modularity added to the browser
somehow, over time.
> 3) Interoperability with other module systems:
Interesting question.
> 4) Implementation difficulty:
That's what we need to find out.
Patrick Mueller - http://muellerware.org/
An include() that does a global eval cannot be *standard* if the
Helma/Pythonic proposal is accepted. The reason it cannot be standard
is that if it is standard then someone might use it in a library that
is "standard compliant" and the whole global clobbering issue returns.
That defeats the purpose of the Helma/Pythonic proposal.
An implementation could have an include() as an extension, of course,
but that might encourage code that is not sharable as the code would
not be standard compliant.
Peter
> On Feb 2, 6:37 pm, Tom Robinson <tlrobin...@gmail.com> wrote:
>> This example was taken from another thread:
>>
>> var greeten = require('greet-en').greet;
>>
>> This is not user friendly at all. I've never encountered a module
>> system that requires you specify the name of the module, the name of
>> each object from the module you wish to import, and the name you want
>> to assign each object to.
>
> Agreed. With Harmony syntax, this gets a bit better:
>
> var {greet} = require("greet-en");
Yes and I think that is admirably JavaScript-ish. The module is
clearly first-class as it is easy to see it being passed around.
> But, I do agree that imports like this should work in deeply legacy
> JavaScript. This syntax is not in our current proposal, but what
> would you think of this syntax:
>
> include("greet-en", ["greet"]);
> or
> include("greet-en", {"greet": "greet"});
If old JavaScript engines need support, just write the destructuring
out in full.
var mod = require('greet-en");
var greet = mod.greet;
Then apply some patients, browsers will catch up, and the code can be
reduced. This is the browser scripting world. Patients is part of the
system.
> from "File" import * // in future-ES
There is no need for the Python-like "from ... import ..." in
JavaScript. Although it doesn't cover the '*' case, the reuse of
(destructuring) assignment is sufficient and allows nested
destructuring which the above syntax doesn't.
>> Code should be properly namespaced to prevent collisions. We can
>> enforce it in the package management repository if we want.
>
> But there's nothing like actually having sovereignty in your own name
> spaced file to give you peace of mind, especially when you're sharing
> your name space with unknown, potentially malicious peers.
Or the monkey-patcher on the team.
>> 2) Interoperability with the browser:
>
> Summarily, it is possible to implement module loaders that manipulate
> the lexical scope in modern browsers. I'll get to how later, but we
> have a prototype.
>
>
>> 3) Interoperability with other module systems:
>
>> I suspect many libraries that have their own module system (Dojo?
>> YUI?) would be incompatible with anything as complicated as the
>> proposed system.
>
> Mark Miller pointed out last week that Dojo and YUI would have to
> defer to an external loader mechanism to be interoperable with Caja.
> To that end, we're hoping to develop out our transitional syntax so
> that Dojo and YUI could be refactored to not only work inside a module
> loader, but also in global scripts.
>
>> And, as noted in the other thread, people are working on adding a
>> module system to the official ECMA spec. It would be a shame if we
>> came up with our own system that was incompatible with the official
>> one (if it's ever actually added to the spec).
>
> That's us, and we're very interested in crafting a design that would
> be interoperable with the systems you're building.
Interoperable is a great goal.
Possibly a note of pragmatism: My guess is that it will be a very long
time (~10 years) before the fourth edition of ECMAScript is proposed,
finalized, approved, implemented, and ubiquitous in browsers. If there
is an ES3.2 it could even be longer. There is no way to know now if a
current module proposal will be standardized. We might not even be
programmers when it can be used in the browser. The programs we are
writing will likely be replaced by then. ECMAScript version will also
be opt-in as part of the script tag's type attribute value so current
code will continue working.
Although interoperability is a great goal but none of use have a
crystal ball. Macromedia thought it did and jumped the gun
implementing the original proposed ECMAScript with classes. That
became the ES4 proposal and was shot down partly due to the packages,
namespaces and units complexity.
Peter
That creates a local "print" variable but it is not shown as such.
Explicit is better, in many people's opinion.
What if there is a "print" variable in the module doing the requiring.
It gets clobbered by the above require.
Also is that a path/to.js module with a module.print property? Or is
that a path/to/module.js module with a "print" property? It is
ambiguous.
> print('hi there!);
Peter
If for no other reason that its by far the simplest thing to do, I
strongly recommend adopting the require() function which takes a file
path, and ensures only loading once. For people who really want the
ability to reload a file, either add an unload() method, or an
optional second parameter.
Benefits:
1) Simple. Familiar to anyone who has programmed in any C style
language.
2) Browser compatible. It's trivial to implement this method as a
synchronous (or async) XHR in the browser.
3) Easy to implement, very difficult to implement wrong.
I can see that. I can also see how implicit conventions might be
beneficial too. Less code. Of course I could always implement a req
function that does things the way I like em so it really is not a big
concern.
> What if there is a "print" variable in the module doing the requiring.
> It gets clobbered by the above require.
Right, though I would like the design to assume developers understand
the rope they have is plenty useful and dangerous too. Certainly a
rogue could invoke require without thinking of doing something so
clever as:
function() {
require('module.print');
}();
or perhaps...
var SuperRad = function() {
require('module.print');
this.print = print;
};
SuperRad.prototype.choice = function(apples, oranges) {
this.print('bananas!')
};
...etc... coming from the Ruby world this is always mitigated by the
author correctly scoping their own libs. *shrug.
> Also is that a path/to.js module with a module.print property? Or is
> that a path/to/module.js module with a "print" property? It is
> ambiguous.
Had not considered this. The browser would say, 'wtf is this' in the
same voice benitio del toro used in fear in loathing. Agree w/ above
and I think explicitly including a js and having the author know 'the
right thing to do' library wise feels correct to me.
require('path/to/module.js'); // loads print into the current scope
> 1) The API:
>
> This example was taken from another thread:
>
> var greeten = require('greet-en').greet;
>
> This is not user friendly at all. I've never encountered a module
> system that requires you specify the name of the module, the name of
> each object from the module you wish to import, and the name you want
> to assign each object to.
>
> The VAST majority of the time I just want to do:
>
> require("File")
>
> and have access to the File object.
>
>
> I'm interested in hearing if others agree, and if you don't, why not.
>
I like the pytohnic modules I agree, but I also agree with you.
If we want to follow pythonic modules, the following should be
allowed:
var greetings = require('greetings').*
or
var greetings = require('greetings').all
or simply
var greetings = require('greetings')
This would be similar to the pythonic 'from module x import *'
You then could use greetings.greeten(); or to achieve
the original result do var greeten = greeting.greeten;
-- MV
>> 4) Implementation difficulty:
>>
>> Are we sure we can implement the proposed system in all the major
>> interpreters without too much trouble? ondras mentioned in IRC
>> yesterday that V8 doesn't support executing code with arbitrary
>> scopes, as is done in the example implementation on the Helma site .
>> There may be a workaround, I don't know.
>
> Yes, ondras told be about that on IRC today. IMO this is a real
> shortcoming in the V8 API, which seems to be tailored exactly for use
> in Chrome and nothing else. But I understand it's a showstopper until
> there's a way around it.
I don't think that is a show stopper. It is not necessary to use V8.
Peter
IMHO this shows that we should focus on other issues (we have lots of
them) and freeze the module-related stuff. At least until we can know
V8 team's general attitude regarding this problematics.
Is it possible to do a pure JS implementation that does not provide
"missing var" protection but does at least conform to the basic
interface? I would prefer having an imperfect solution running on v8
than nothing at all.
Kevin
--
Kevin Dangoor
email: k...@blazingthings.com
blog: http://www.BlueSkyOnMars.com
> On Tue, Feb 3, 2009 at 8:47 AM, Ondrej Zara <ondre...@gmail.com>
> wrote:
>> IMHO this shows that we should focus on other issues (we have lots of
>> them) and freeze the module-related stuff. At least until we can know
>> V8 team's general attitude regarding this problematics.
>
> Is it possible to do a pure JS implementation that does not provide
> "missing var" protection but does at least conform to the basic
> interface? I would prefer having an imperfect solution running on v8
> than nothing at all.
I've been playing with "module" stuff a bit in JS, and this was the
trade-off that I took as well. Play some games, wrapping wad of JS
with a function closure/etc, which can then be eval()'d, but doesn't
provide protection from folks who forget the "var". Basically, it
works if you write your code correctly. Compared to if you use Rhino
and actually CAN provide the protection.
A trade-off I'm willing to live with for now.
My assumption is, if this "module" concept has legs, then we'll have
this capability natively in all the JS VM's eventually that do the
right thing.
It already is. But it's not as if we invented the idea ourselves. This
is what php has done forever. This is, essentially, what we already
have in JavaScript. This is almost certainly the most used way to
import code that there is.
> For the rest of us, proper namespacing is important, do not
> underestimate it. The module system you propose is sinking the modern
> web. It is not sustainable. Doesn't scale. It's better to trust an
> indelible rule of the standard/implementation, than trust the good
> behavior of thousands of individual developers.
Sinking the modern web? The web seems to be doing just fine with
global variables. Honestly, I can't think of a single instance in the
last 6 years I've been doing this where I ran into a serious namespace
collision. I'm not saying it doesn't happen, but I am suggesting its
not nearly as common as say. And jQuery, almost certainly the most
popular JavaScript framework, makes a point of being well behaved with
global variables. Are the two related? Maybe, maybe not. But to claim
that the modern web is sinking seems silly.
> If I decide that I want to change over from using the
> (printify) module, to using the (SFprintable) module, which happens to
> have the same API, but implements new feature X Y Z, and improves
> performance, I only have to change one line of code (the require), and
> all the REFERENCES to that library stay the same throughout the rest
> of my code.
This sounds like a huge red herring to me. How often are two libraries
entirely API compatible but use different method names? And if you're
really concerned, you can always implement a shim around any library.
In all honesty, I don't necessarily think a module system is a bad
idea. I just think its a terrible starting point. We're already seeing
reports that the proposed module system is not possible in V8. If the
goal of this discussion is to come up with something that we can
actually convince environments to implement, the more complex it
becomes the less likely we are to succeed.
On Tue, Feb 3, 2009 at 2:51 AM, Hannes Wallnoefer <han...@gmail.com> wrote:
> Yes, ondras told be about that on IRC today. IMO this is a real
> shortcoming in the V8 API, which seems to be tailored exactly for use
> in Chrome and nothing else. But I understand it's a showstopper until
> there's a way around it.
1. As I understand it, you mean V8 provides only static global scope
per OS process?
[ This would make sense, per your comment, since Chrome uses OS
processes extensively as a means of isolation, but I want to make sure
I understand. ]
2. Could you provide some more details of the API deficiency in question?
Thank you!!!
Ihab
--
Ihab A.B. Awad, Palo Alto, CA
On Tue, Feb 3, 2009 at 8:26 AM, <ihab...@gmail.com> wrote:
> 1. As I understand it, you mean V8 provides only ONE static global scope
> per OS process?
I missed the word "one".
I completely support this. From what I know, different libraries with
similar functionality always offer different names for their classes /
objects.
Ondrej
Actually IIRC the whole issue became a problem because of the
collision between JQuery and prototype and the use of the $ object in
the global namespace. I think everyone just got antsy after that and
the rule became 'thou shall not pollute the global namespace', it's a
good rule, but I think we should allow for well defined exceptions to
it, and move on.
I dont think it is a big problem for the most part, as we are
intending to produce well behaved modules, which should be tested
against global namespace additions by doing a before and after
walkthrough of the global object in the test suites.
for example, i'm not too worried about require living there as I think
(like in the case of JSON) it will eventually be implemented natively
in the VMs. We get the opportunity to describe the feature. I kind of
see the module thing playing out that way and perhaps even some of the
other API's like File.
My worry about what I see in the current discussions is a kind of
overreach, I think we should just try to find as simple an
implementation as we can and backburner much of the esoteric
discussion. Using the python pattern ok for ideas, but as JS is not
python I'd be wary of any effort to implement that in JS (same goes
for Ruby and Php) although I betting even that'll be tricky.
On Tue, Feb 3, 2009 at 8:26 AM, <ihab...@gmail.com> wrote:
> Hi Hannes and Ondras,
s/Ondras/Ondrej/ -- my apologies.
So it seems you can create a new global scope, ensure its "Array"
points to a shared one, then run your code in that. And it seems like,
at the end of the thread, you were happy. :)
What is the missing part?
If I understand correctly, a pure JS implementation without "missing
var" protection *is* possible with Kris (Kowal) and Ihab's proposal,
but not the Pythonic / Helma modules. This is primarily because you
explicitly specify which things are exported by adding them to the
"export" variable.
(An alternative to the "export" object in Kris/Ihab's proposal is
using "this" as the export object, as Hannes mentioned. But I think
they're essentially equivalent, it's just a matter of if you want to
use "this" or "export")
Since the module can be wrapped in a function when loaded, if you
"var" everything when using the Kris/Ihab module system the code can
be reused on pure JS implementations (in the browser or on the server)
without polluting the global namespace, but if you use it with a
"real" implementation you get the global pollution protection too.
Seems like a pretty good tradeoff to me. Sort of a graceful
degradation. The standard library will certainly be scrutinized enough
to ensure there aren't any missing vars.
In the Pythonic/Helma modules anything that's added to the module's
scope is implicitly "exported". IMO this is a huge drawback. As
mentioned in another message, there's no way to "scrape" these
implicitly exported properties in pure JS implementations, and they
would pollute the global scope if you loaded them on the client.
Also, if I understand correctly, this means anything you import into a
module also gets *automatically exported* even if there's no reason
for it to be. This makes the "include" helper function completely
useless, since you're now (recursively) adding every property of every
included module to your module's scope, negating the purpose of the
module system. Or am I missing something?
This could be mitigated by wrapping the module in a function, and
using "var" for things you don't want to export, but it feels like a
hack to me.
I still think a complicated module system is beyond the scope of this
project, but Kris/Ihab's proposal addresses many of my original
concerns. It sounds like they've thought about this problem a *lot*. I
might be willing to support their proposal, but not the Pythonic
modules proposal. At least not without some changes.
-Tom
> I still think a complicated module system is beyond the scope of this
> project,
The goal of the group is to enable writing sharable code. A module
loading system is central to that goal because sharable code may load
other sharable code. That is, the module loading system will appear
directly in sharable code.
Peter
It is not prerequisite to discussions about APIs. It is definitely
prerequisite to creating actual sharable library files which is what
we all really want in the end...actual code we can download and use.
Peter
I see. Yeah, so far, Kris and I have imagined that we would sequester
away any concept of "the" global scope in favor of hygiene.