Jonathan,
I’d like to hear about the benefits of JSMOD’s syntax, as compared to my $import and $from functions. From what I can tell, both do basically the same thing – both use eval() to generate a bunch of “var” statements to import a number of items into the current scope. It seem to me that the main difference is syntax.
Import Syntax
The $import / $from syntax maps more closely to the python functions (which IMO is important for py2js), plus it is more explicit and command-like – you’re running a function to import things, rather than declaring a list of imports. With JSMOD, the actual command that does the imports comes later – “eval(__)” – which doesn’t seem as intuitive...
Module Declaration Syntax
With JSMOD, you set the name to a string and let JSMOD implicitly save your module to an inner object. Again, with the $import / $from syntax, things more explicit. It’s being done the same way many Javascript libraries handle namespaces – simply using nested objects: “$import.my_module = …” and “$import.my_module.my_submodule = …”.
The only advantage of the indirect syntax that I can see (please let me know if there are more) is preventing users of the libraries from messing things up for others. With the $import/$from syntax, one piece of code could set $import.foo = null; and thereby screw up another piece of code that was going to import foo later and now can’t. However, we’re talking about two pieces of script running on the same page – there are much easier ways scripts can mess each other up, like blowing away the global $import or JSMOD object, for instance. It’s simply not possible to totally sandbox client-side scripts to protect them from each-other.
If you’re thinking about server-side Javascript, in CommonJS there’s a require function for that – but it’s not a good fit for client-side Javascript.
For client-side javascript, the most important thing module authors can do is wrap their stuff in an anonymous function and only expose the functions and classes that they want to expose. IMO, the amount of additional sandboxing you get from hiding the imports in an inner object really isn’t that much – and (again IMO) the less intuitive syntax isn’t worth the slight gain in sandboxing.
-- peter
> I want something consistent with CommonJS
> but which is a good fit for client side programming
FYI: The abandoned ECMAScript 4 spec specified “Modularity, name hiding and library construction” by packages and namespaces. While many ECMAScript 4 ideas were brought forward to the ECMAScript 6 specification, according to wikipedia, the packages and namespaces were not.
While the CommonJS spec both (synchronously) loads the desired module and imports it into the current scope – and this is what Python does and I think it’s wise for a server-side system – the ECMAScript 4 spec only focuses on the latter (importing variables into the current scope). Because of the latency of client-side connections and the variety of loading and packaging mechanisms already available, I think it’s wise to separate concerns and let the loading be handled separately.
Note that the ECMAScript 4 spec is nicely declarative and clear. Declaring a module as part of a namespace looks like this:
package org.ecmascript.experiment {
internal var v;
}
package org.ecmascript.experiment {
public function f(k) { v+= k; return v-1 }
}
And importing f (and everything else in experiment) looks like this:
import org.ecmascript.experiment.*
If we were to convert this into today’s Javascript, declaring the module would look something like this:
var org = or || {};
org.ecmascript = org.ecmascript || {};
org.ecmascript.experiment = (function() {
var v;
function f(k) { v += k; return v-1 }
return { f: f };
})();
And importing would look something like this (using $import because import is a reserved word):
eval($import('org.ecmascript.experiment.*'));
The CommonJS specification examples come with “Browser Sharable” versions, here’s the “protect the global namespace” version that handles everything inside a function require.install(). It looks like this:
//
// math.js
//
(function(){
var mod = function(require,exports) {
exports.add = function() {
var sum = 0, i = 0, args = arguments, l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
};
require.install ? require.install('math', mod) : mod(require, exports);
})();
Creating a 2nd module that uses the math module above looks like this:
//// increment.js//(function(){
var mod = function(require,exports) {
var add = require('math').add;
exports.increment = function(val) {
return add(val, 1);
};
};
require.install ? require.install('increment', mod) : mod(require, exports);
})();
Creating a program that uses the increment module above looks like this:
//// program.js//var inc = require('increment').increment;
var a = 1;
inc(a); // 2
And here’s the de-facto standard of the web today, where each library hangs everything off their own global library object:
// define the library in a file called asdf.js// first and last lines are boilerplate var LIB = LIB || {};
LIB.asdf = (function() {
var exports={};
var c = 1;
exports.a = function() {
return c;
};
exports.b = 2;
return exports;
})();
// use the library in the browserLIB.asdf.a();
// use the library on the serverrequire('asdf').LIB.asdf.a();
// Use the library on the server or in browser.// A module dependent on asdf would need to // be written like this or something similar.(require ? require('asdf') : window).LIB.asdf.a();
If we’re to promote an import mechanism that we’d like to have widespread acceptance, it seems it should at least be able to import from modules defined in the de-facto standard (from a library global object), even if it doesn’t export that way and doesn’t encourage module authors to write their modules in that way.
-- peter
I’d like to hear about the benefits of JSMOD’s syntax, as compared to my $import and $from functions. From what I can tell, both do basically the same thing – both use eval() to generate a bunch of “var” statements to import a number of items into the current scope. It seem to me that the main difference is syntax.
Import Syntax
The $import / $from syntax maps more closely to the python functions (which IMO is important for py2js), plus it is more explicit and command-like – you’re running a function to import things, rather than declaring a list of imports. With JSMOD, the actual command that does the imports comes later – “eval(__)” – which doesn’t seem as intuitive...
Module Declaration Syntax
With JSMOD, you set the name to a string and let JSMOD implicitly save your module to an inner object. Again, with the $import / $from syntax, things more explicit. It’s being done the same way many Javascript libraries handle namespaces – simply using nested objects: “$import.my_module = …” and “$import.my_module.my_submodule = …”.
The only advantage of the indirect syntax that I can see (please let me know if there are more) is preventing users of the libraries from messing things up for others. With the $import/$from syntax, one piece of code could set $import.foo = null; and thereby screw up another piece of code that was going to import foo later and now can’t. However, we’re talking about two pieces of script running on the same page – there are much easier ways scripts can mess each other up, like blowing away the global $import or JSMOD object, for instance. It’s simply not possible to totally sandbox client-side scripts to protect them from each-other.
If you’re thinking about server-side Javascript, in CommonJS there’s a require function for that – but it’s not a good fit for client-side Javascript.
For client-side javascript, the most important thing module authors can do is wrap their stuff in an anonymous function and only expose the functions and classes that they want to expose. IMO, the amount of additional sandboxing you get from hiding the imports in an inner object really isn’t that much – and (again IMO) the less intuitive syntax isn’t worth the slight gain in sandboxing.
> I want something consistent with CommonJS
> but which is a good fit for client side programming
FYI: The abandoned ECMAScript 4 spec specified “Modularity, name hiding and library construction” by packages and namespaces. While many ECMAScript 4 ideas were brought forward to the ECMAScript 6 specification, according to wikipedia, the packages and namespaces were not.