Looking at some slides on JS 2,
I think it mostly looks pretty good -- the type stuff seems complex,
but at least not terribly intrusive. There's one thing I don't like:
package org.mozilla.venkman {
class Debugger { . . . }
class Inspector { . . . }
class StackWalker { . . . }
}
I think package names as a whole just don't make sense; when I do x
= 1 then 1 doesn't know it is named x, the same should apply
to packages. Package names bother me in Python, but even more in this
explicit form (with a package declaration).
Like Python, all scripts should be modules (in Python terminology),
automatically. That is, you don't choose whether or not you are
creating a module, you just are, always.
Unlike Python, packages shouldn't get names. Dealing with locating
packages, resolving conflicts or dependencies, etc., is just tedious
and unnecessary. Instead, use something like:
venkman = import('venkman.js')
This loads the given script by URI (relative to the script itself) as
a module, and returns that module. Mostly this is just dead simple
and obvious, and naturally safe. Maybe it should be syntax, as
import * is still a reasonable operation, especially for backward
compatibility. Or maybe syntax like var * = import('venkman.js') to
unpack an object (in this case a module) into the local namespace.
Like in Python, all modules should know where they are located,
similar to __file__. Maybe a __uri__ variable. All relative imports are relative to this location. This
way you can do simple factoring without worrying about "name", just
relative structure of your package. Unlike in Python, there's no
__name__.
There still needs to be a way to include external libraries (which
wouldn't necessarily be accessible through relative paths). This is a
similar distinction of C's #include "foo.c" and #include
<foo.c>. Maybe import('venkman.js', 'venkman.mozilla.org'),
where the second argument is the name of the framework or package
(venkman.js is the name of the module). I suppose
org.mozilla.venkman looks nice as a package name to people who
program Java; but venkman.mozilla.org looks nice to people who
surf the web.
This second form of importing requires some way to find the libraries
(which is just as true for explicitly declared packages, or maybe even
worse). I imagine some routine like loadPackage(filename,
packageName). This could return a package or null -- if
null then the next loader would be tried. So, an example:
registerLoader(function (filename, packageName) {
try {
return import('/js-lib/' + packageName + '/' + filename);
} catch (e if e isinstance ImportError) {
return null;
}
});
This would give a global search path of /js-lib/. Some of these
kinds of loaders should be easy to create (implemented as standard functions), perhaps
declaratively through a <link>, since way to find scripts is
intrinsic to the site, not the script. Whether loading the same
script/URI twice produces the same module, I'm not sure -- probably.
Some languages distinguish between the two (e.g., require
vs. include in PHP).
Packages could install local loaders. So you could package your
library with all its dependencies, and register a loader for that,
coming after all the other loaders.
Maybe in addition to importing by package, version should also be
allowed. So, import(filename[, packageName[, version]]). Though
it would have to be a version specification, like >=1.0,<2.0`, not
just a single version... and that's relatively hard to do, since
there's no generic way in plain HTTP to list the contents of a
directory and do a search. I'd be inclined to leave that out, maybe
allowing for:
venkman = import('venkman.js', 'venkman.mozilla.org');
if (venkman.__version__ < '1.0' || venkman.__version__ >= '2.0') {
venkman = import('./packages/venkman-1.0/venkman.js');
}
Or some externally-provided search path, e.g.:
{
// fancyDebugger is picky about the version of venkman it uses...
let loader = registerLoader(
pathImporter('./packages/venkman-1.0'));
fancyDebugger = import('fancyDebugger.js');
deregisterLoader(loader);
}
Which reminds me that JS2 should include encapsulated initialization/finalization. This would be
another kind of anti-lesson from Python -- we've taken too long to add
this (with in Python 2.5), and Javascript shouldn't put it off.
It's easier to build Javascript function inline and pass them around,
but it's still not used a great deal, in part because it requires a
functional style that is unfamiliar to most programmers. Maybe it
should learn from Ruby's block arguments, though I'm not sure if the
specifics translate well. Maybe:
withLoader(pathImporter('./packages/venkman-1.0'), {{
fancyDebugger = import('fancyDebugger.js');
}})
That specific syntax doesn't really appeal to me, but there must be
something like it that could work. The way JS2 uses let feels
similar, but doesn't actually work in the same way.