Re: Module loading discussion
Monty Zukowski <monty-aaXZSQuah4Ac3H1WRXY/HAC/[email protected]>
| Newsgroups | gmane.comp.lang.e.general |
|---|---|
| Message-ID | <[email protected]> |
I like the ability to bind variables during instantiation, seems to follow along with the philosophy of http://bracha.org/newspeak-modules.pdf which I found to be helpful in reasoning about the module system we're developing at Temboo. Monty On Thu, Nov 19, 2009 at 6:10 PM, <[email protected]> wrote: > Retweeting a message from google-caja-discuss to e-lang. > > * * * * * > > Caja fans, > A couple days ago, I presented to the Caja group some material that I had > previously presented to the ECMA TC39 committee, describing the module > system we have implemented in Caja. The slides are here: > http://sites.google.com/site/ihabawad/Home/es5Modules-2009-11-06.pdf > * * * * * > At the Cajita level, we implement a module loader such that the result of > calling: > load('foo') > where "foo" is a module ID, returns a "module function". This is a closed > function (i.e., it has no free variables) and is instantiated by calling it > with an object literal providing bindings for its free variables. So if > "foo.js" contained, say: > x + y; > then the following expression: > load('foo')({ x: 3, y: 4 }); > would evaluate to 7. So far so good. > One desideratum is that the IDs of modules are "self-relative". Let's say > module "a/b/c.js" contains the following expression: > load('../d/e'); > This should be relative to its _own_ ID; hence, the result of this should be > to load: > a/d/e.js > This means that a module must "know" its own ID, in some sense, and hand it > to its own loaders so that they can compute IDs relative to that. > The way we did that in Cajita, we just _gave_ the module access to its own > loader. But Mark Miller pointed out correctly that this was a violation of > the assumption that module functions are transitively immutable -- i.e., > powerless. If a module function is connected to something that, at the mere > utterance of "load()", goes out to the internets and fetches guff, that is > definitely an ambient authority. So, what's to do? > The simplest way to fix this is to allow a module to know its own ID (i.e., > the ID it was loaded by). Let's say each module function is given a > well-known constant in its lexical scope; for this description, we will call > it: > _thisModuleId_ > A module can then load something relative to its own ID by saying: > load('foo', _thisModuleId_); > and thus the module function does not have to close over its loader. > Capability security regained. The specific syntax of this sort of thing > remains to be hashed out. > * * * * * > Mark Miller made some concrete suggestions as well. He stipulated two > "load()" forms, which we name for the purposes of this discussion only. > "loadf" stands for "load function" and is the basic "load()" we have now. To > load a module function, do: > moduleFunction = loadf('a/b/c'); > which will load "a/b/c.js" as before. He also proposed "loadi", which stands > for "load instance" and actually instantiates the module, in addition to > loading its module function. To use it, do: > moduleInstance = loadi('a/b/c', { x: 3, y: 4 }); > The trick with "loadi" is that it has two conveniences: (a) it loads a > module function and instantiates it in one shot; and (b) it desugars to: > moduleInstance = loadf('a/b/c')({ loadf: loadf.for('a/b/c'), x: 3, y: 4}); > In other words, it automatically passes down to the module being loaded a > version of the current loader that is pre-configured to search relative to > the path "a/b/c". > The reason why conveniences (a) and (b) are mixed together is that I may > call any given module function with two different loaders, so providing a > loader is an instantiation time, not a loading time, thing. So say I load > some module function: > mf = loadf('a/b/c'); > I can instantiate this with two different loaders: > mi_1 = mf({ loadf: theFirstLoader, x: 3, y: 4 }); > mi_2 = mf({ loadf: theSecondLoader, x: 3, y: 4 }); > and the object graphs created in mi_1 and mi_2, including the code they are > transitively connected to, may be wildly different because -- well -- they > were instantiated with different loaders. > There is a final wrinkle in this. Note that we implement synchronous > "load()" on top of an async loader by stipulating that (i) the topmost > module loading is always async; and (ii) sync dependencies are declared in > the Caja module record, and are thus prefetched prior to calling the module. > Thus the sync dependencies are already in the loader's cache when the code > is running. Now, what if an instantiating entity switches loaders along the > way, and the loader provided does _not_ have the requested module in the > cache? The answer is => this is a predictable failure mode. An exception is > thrown. This is, after all, a fairly uncommon case. > Cheers, > Ihab > -- > Ihab A.B. Awad, Palo Alto, CA > > _______________________________________________ > e-lang mailing list > [email protected] > http://www.eros-os.org/mailman/listinfo/e-lang > >