simple modules
David and Sam, well done! Lots of nits to pick and ideas to discuss but this makes it very clear how a simple "static" (or 2nd class) module system could nicely integrate into ECMAScript.
Comments later...
Kris, my apologies for misspelling your name!
& Sam,
Could you please add to your examples the following case: a module which keeps an internal counter of how many times one of its methods is called. In other words, in the body of the module, there is something like:
var i = 0;
export function get() { return i++; }
Could you also please add an example where the variable "i" is initialized to a value supplied by the module importer?
Ihab
On Jan 29, 2010, at 6:50 PM, Allen Wirfs-Brock wrote:
David and Sam, well done! Lots of nits to pick and ideas to discuss but this makes it very
clear how a simple "static" (or 2nd class) module system could
nicely integrate into ECMAScript.
I wanted to mention that "second class" is not a bad thing, since
modules are static (source-level, prior to loading and compilation)
segregations of code with generally or mostly static dependencies.
Also, Sam and Dave's proposal reflects second class modules as
objects, so there is no iron curtain between second and first class --
but crucially, the system is static if you don't reflect a module as
an object, which makes it easier to use for common module chores.
Even the first-class alternative needs special forms such as import
"..." so that static analysis can arrange to prefetch needed
resources. This makes for tension between the static and dynamic
"poles", since an import in a deeply-nested if-then chain, at least in
the browser, must still have its module prefetched unconditionally to
avoid violating the run-to-completion execution model.
Of course, one can use XMLHttpRequest and a safe eval, or (let's hope)
a cleaner dynamic import function, in either a first- or second-class
system. But this seems to be an exceptional case, and the import ["A",
"B", ...] form in Sam&Dave's proposal covers the common fail-over/
fallback use-case without requiring random logic driving dynamic import.
Another observation: first-class-only modules tend to fall into the
first-class functions gravity well. We saw this in presentations from
Kris and Ihab at the TC39 meeting, with the |module point(x, y) {...}|
example, which closely resembled a named function expression. Nothing
wrong with module as lexical scope special form, but if it makes a
module constructor function, then it to some extent duplicates function.
We already have first-class functions, but so far we have had to rely
on the <script> tag, eval of XHR-loaded code, pre-deployment
translation tools, and other extraordinary measures in lieu of an
integrated module system. This is the way things are, for better or
worse. But it need not dictate how first-class (first-class-only) an
integrated system should be.
That JS developers have to "make do" via a combination of first-class
functions and dynamic loading methods, however important to support
downrev browsers, should not preclude static module systems from
being explored. Good to see this second-class module system
exploration, at last.
I'll add an example demonstrating modules with shared state; if you want to make a point about the consequences of this, I'm happy to discuss.
WRT isolation, Sam and I have some thoughts about a largely orthogonal proposal for providing isolation without needing to impose restrictions on the semantics of modules. I will ping the list as soon as that's ready.
Thanks,
On Fri, Jan 29, 2010 at 5:13 PM, David Herman <dherman at mozilla.com> wrote:
We had a good discussion about modules at this week's meeting, and Sam Tobin-Hochstadt and I have worked out a strawman for a simple module system. I've posted a first draft to the wiki:
There are lots of examples to peruse at:
strawman:simple_modules_examples
which might also be a decent starting point to get a feel for the design.
Thanks for the examples page. I think it would be useful to have comparable examples for the other 3 modules strawmen too.
This has a good bit of overlap with -- and is in many ways inspired by -- Ihab and Chris's work, but it has at least one major departure: modules in this proposal are not inherently first class values, although we do provide ways of getting objects that reflect the contents of modules.
So, as Brendan described, they're not first class because you want to analyze dependencies statically?
I agree that's a really common requirement. I've found that to be a problematic area of CommonJS modules, but I think it can be resolved pretty easily. I will write something about this, but basically I just have a special syntactic restriction on require() statements. "embeddable" modules are a subset of modules, and are statically analyzable.
The 3 use cases I see for statically analyzing dependencies are:
- async/parallel module retrieval over HTTP (need to retrieve modules before evaluating them)
- static "linking" (what code.google.com/p/gelatin-js does, need to get dependencies without executing the program)
- parameterized modules / module metaprogramming (need to get dependencies first for exposing to users)
For comparison, I would point to the fact Python has evolved a crazy number of module metaprogramming facilities over the years. It isn't obvious why, but basically any large Python program does something "weird" with modules in my experience (i.e. involving a Turing complete language):
docs.python.org/library/modules.html#importing-modules
thanks, Andy
On Sat, Jan 30, 2010 at 9:46 AM, Dave Herman <dherman at mozilla.com> wrote:
I'll add an example demonstrating modules with shared state; if you want to make a point about the consequences of this, I'm happy to discuss.
Ok thanks. I assume your longer description notes what the state would be as seen from two independent usages of module Counter?
Also: Under your proposal, is it possible for the initial value of "counter" to be supplied by the client of the module? (The answer to this could be clear from the answer to the previous item.)
Ihab
So, as Brendan described, they're not first class because you want to analyze dependencies statically?
A number of reasons:
- the dependency graph is statically computable
- first-class modules duplicate existing expressiveness of functions and objects
- exposing more information to the compiler increases optimization opportunities, particularly parallelization
- modules encapsulate bindings
The consequence of the latter point is that in our strawman, modules are a lexical scoping mechanism-- they let programmers divide up scoped regions into manageable pieces and still use lexical (ie, static) scope to refer between one another. This is not to say that you can't have a first-class module system that still manages static information like bindings, but it's hard to keep it simple, which was one of our goals.
More abstractly, I'd say that 2nd-class modules embody the notion of modules as units of deployment and units of definition. These are both commonly static concerns. But when they need to be determined dynamically, you pull out the power tools like eval' or
loadModule'.
Ok thanks. I assume your longer description notes what the state would be as seen from two independent usages of module Counter?
I may not have made this clear enough in the strawman. A module is evaluated only once, so two independent usages will share state.
Now, this is "per context" -- the idea of our not-yet-written-nor-even-fully-conceived strawman on isolation would be that you could create a new Context in which modules could be independently instantiated. But within a given context, modules are never instantiated more than once.
Also: Under your proposal, is it possible for the initial value of "counter" to be supplied by the client of the module?
Not directly; modules are not parameterized. If you want parameterization or multiple independent stateful values, you just use functions, objects and lexical scope, as usual:
module Counter { export function makeCounter(initial) { var state = initial; return { current: function() { return state }, inc: function() { return state++ } }; }; }
& Sam,
First of all, this is an extremely well-written proposal and was a pleasure to read; thanks for putting it out there.
My first set of questions are not (at least, directly) about the first- or second-class nature of the modules as you propose them, but rather about the isolation model implicit. Within each Context, naming a module is equivalent to reaching a shared instance of it. This instance must be declared at the topmost scope of the importing module, so it is in some sense forced on the entire lexical scope of that module. This means that, to achieve isolation via lexical scopes, we must use Contexts, implying the use of async APIs. What is the rationale for this?
Another, related question is how you recommend linking in things like the standard libraries, wherein module names referring to code (internal linking) are in the same namespace as module names referring to powerful objects provided by the platform (external linking).
Ihab
On Sun, Jan 31, 2010 at 7:57 PM, <ihab.awad at gmail.com> wrote:
Hi Dave & Sam,
First of all, this is an extremely well-written proposal and was a pleasure to read; thanks for putting it out there.
Thanks!
My first set of questions are not (at least, directly) about the first- or second-class nature of the modules as you propose them, but rather about the isolation model implicit. Within each Context, naming a module is equivalent to reaching a shared instance of it. This instance must be declared at the topmost scope of the importing module, so it is in some sense forced on the entire lexical scope of that module. This means that, to achieve isolation via lexical scopes, we must use Contexts, implying the use of async APIs. What is the rationale for this?
First, it's also possible to achieve isolation via lexical scope by using functions, closures, and objects, as Dave's example shows. Our proposal attempts to leverage the existing facilities in EcmaScript to achieve most of isolation, using modules for name management and restricting the top-level lexical environment.
Second, we expect that needing to isolate untrusted code is the
uncommon case for importing a module. Therefore, making the common
case simple implies that just using import' should avoid the details of isolation. When it's important to isolate some piece of code, that code will often be from another site, or otherwise created dynamically from the perspective for the loading program. Therefore, we felt that integrating isolation with the dynamic
loadModule' is the right
place.
Third, the reason the API for loadModule' is async is that encouraging synchronous dynamic loading on the web is encouraging broken webapps. However, it might not be right for
loadModule' to be
standardized in ES, as Dave mentioned, and offline applications would
want a synchronous version.
Another, related question is how you recommend linking in things like the standard libraries, wherein module names referring to code (internal linking) are in the same namespace as module names referring to powerful objects provided by the platform (external linking).
So, the way I think of it, everything in our proposal is internally linked, except insofar as a Context might specify a different behavior for the name of some module. I would recommend either using a Context-style facility for preventing or limiting access to powerful objects, as you put it, or explicit wiring together of code using objects and functions. This means that in the common case, where a simple ES program doesn't deal with trust at all, the DOM and other objects are available just as easily as they are today, but it's also possible to enforce sophisticated security policies.
On Sun, Jan 31, 2010 at 6:56 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
First, it's also possible to achieve isolation via lexical scope by using functions, closures, and objects, as Dave's example shows. Our proposal attempts to leverage the existing facilities in EcmaScript to achieve most of isolation, using modules for name management and restricting the top-level lexical environment.
But, in some module M that imports X, Y and Z, the top-level lexical environment must include access to all the state encapsulated by X, Y and Z.
Second, we expect that needing to isolate untrusted code is the uncommon case for importing a module.
That is only true if you completely trust all the code you and all your coworkers write on a large project. Otherwise, the issue is not isolating untrusted code but isolating the extent of authorities into code. This is important for both security and software engineering.
Third, the reason the API for
loadModule' is async is that encouraging synchronous dynamic loading on the web is encouraging broken webapps. However, it might not be right for
loadModule' to be standardized in ES, as Dave mentioned, and offline applications would want a synchronous version.
I agree that your 'loadModule' (or any such dynamic loading utility) should be async. But this is a question for another time.
So, the way I think of it, everything in our proposal is internally linked, except insofar as a Context might specify a different behavior for the name of some module.
So you propose linking to a standard library using some agreed-upon name. Let's say it's "stdlib". So, when I say:
import stdlib; /* ... my code here ... */
If "stdlib" were some reference to some code which I could make more or less independently of the container (perhaps subject to its permission, but not too much else), then this is internal linking. However, if "stdlib" is not only some code but an object with expected powers that the container must be expected to set up for me ahead of time, that sounds like external linking.
Am I incorrect in my use of the terms?
In any case, my point stands. The namespaces for "pure code being brought in" and "powers granted to me by my container" are being conflated. For the former, you need modules. For the latter, you need some sort of "service registry" or whatever else.
Ihab
Small feedback from users.
This example does not look right. <script type="harmony"> // import everything import "Math"; alert("2π = " + sum(pi, pi)); </script>
It should be
<script type="harmony"> // import everything as Math import "Math";
alert("2π = " + Math.sum(Math.pi, Math.pi)); </script>
We already have "with" for polluting local namespace, and short syntax for such polluting doesn't feel right. Some longer syntax would be better, e.g.
<script type="harmony"> // import everything import "Math" as this;
alert("2π = " + sum(pi, pi)); </script>
Otherwise the proposal looks good for me.
, Vassily
thanks for the feedback.
It should be
<script type="harmony">
// import everything as Math import "Math";
alert("2π = " + Math.sum(Math.pi, Math.pi));
</script>
This is already possible with the `import "Math" as Math' form (which incidentally can easily be compiled to be exactly as efficient). Leaving the "as Math" part implicit doesn't work if the module specifier is not syntactically an identifier:
import "@#$!";
@#$!.mumble("grunch")
We already have "with" for polluting local namespace, and short syntax for such polluting doesn't feel right.
That's an inappropriate comparison, for two critical reasons. First: `with' dynamically changes the environment, so it destroys lexical scope, whereas when you import everything from a second-class module, it is still possible to know statically what bindings are in scope. Second: there's no contention, since it's a static error to import two modules that bind the same name. So conflicts are ruled out, and there's no ambiguity in a valid program.
Now, I recognize that some people feel that stylistically the "import everything" approach is bad /style/, and that programmers ought to list all their imports explicitly. But at the same time, this is still a scripting language, and it has to be convenient. There's a difference between advocating a style and forcing it. And if it imposes too heavy a burden you get nasty unintended consequences (e.g., nobody uses modules at all).
Some longer syntax would be better, e.g.
<script type="harmony">
// import everything import "Math" as this;
alert("2π = " + sum(pi, pi));
</script>
That would only work with at most one module.
On Mon, Feb 1, 2010 at 1:04 AM, <ihab.awad at gmail.com> wrote:
Hi Sam,
On Sun, Jan 31, 2010 at 6:56 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
First, it's also possible to achieve isolation via lexical scope by using functions, closures, and objects, as Dave's example shows. Our proposal attempts to leverage the existing facilities in EcmaScript to achieve most of isolation, using modules for name management and restricting the top-level lexical environment.
But, in some module M that imports X, Y and Z, the top-level lexical environment must include access to all the state encapsulated by X, Y and Z.
That's only the case if X, Y, and Z provide access to their internal state via the bindings they export. For example:
module MemoFun { var table = {}; export function f(x) { if table[x] { return table[x]; } // imagine complex but pure computation here var tmp = x + 7; table[x] = tmp; return tmp; } }
Here, the only access provided to the state is via the function `f'. Outside modules cannot disturb the invariants of the code at all.
Second, we expect that needing to isolate untrusted code is the uncommon case for importing a module.
That is only true if you completely trust all the code you and all your coworkers write on a large project. Otherwise, the issue is not isolating untrusted code but isolating the extent of authorities into code. This is important for both security and software engineering.
It has been my experience that most of the isolation needed between modules can be accomplished by lexical scope. For example, in the MemoFun module, the function `f' doesn't have to trust any other modules that it might import (or that might import it) to avoid interference with its invariants - lexical scope takes care of that.
So, the way I think of it, everything in our proposal is internally linked, except insofar as a Context might specify a different behavior for the name of some module.
So you propose linking to a standard library using some agreed-upon name. Let's say it's "stdlib". So, when I say:
import stdlib; /* ... my code here ... */
If "stdlib" were some reference to some code which I could make more or less independently of the container (perhaps subject to its permission, but not too much else), then this is internal linking. However, if "stdlib" is not only some code but an object with expected powers that the container must be expected to set up for me ahead of time, that sounds like external linking.
Am I incorrect in my use of the terms?
I would normally say that a module that imports the standard library in that fashion is internally linked. To take a different language, import in Java is internal linking, even though the meaning of names can be rearranged via the classpath (or via the filesystem). But given that we always have the power to change the environment the program operates in, the boundary between internal and external linking can become fuzzy.
In any case, my point stands. The namespaces for "pure code being brought in" and "powers granted to me by my container" are being conflated. For the former, you need modules. For the latter, you need some sort of "service registry" or whatever else.
I think that in many cases, conflating these two concepts is appropriate. For example, if I import jQuery to add some simple bit of functionality to my homepage, I almost certainly want to give it access to all of the things I have access to. This is the common case that our strawman tries to make simple. However, in other contexts a service registry may be more appropriate, and that can be built on top of modules, using either the dynamic loading features, or simple lexical scope with functions and objects.
-- sam th samth at ccs.neu.edu
On Feb 1, 2010, at 9:21 AM, Sam Tobin-Hochstadt wrote:
On Mon, Feb 1, 2010 at 1:04 AM, <ihab.awad at gmail.com> wrote:
But, in some module M that imports X, Y and Z, the top-level lexical environment must include access to all the state encapsulated by X, Y and Z.
That's only the case if X, Y, and Z provide access to their internal state via the bindings they export. For example:
module MemoFun { var table = {}; export function f(x) { if table[x] { return table[x]; } // imagine complex but pure computation here var tmp = x + 7; table[x] = tmp; return tmp; } }
Here, the only access provided to the state is via the function `f'. Outside modules cannot disturb the invariants of the code at all.
This is a crucial point, probably worth repeating: each module has its
own lexical binding environment at its top level, not a global object.
In the Harmony era wiki, this idea goes back at least as far as
As in the "use lexical scope" proposal, the new syntax of |module id
{...}| changes the rules within the braces: no more global object.
In any case, my point stands. The namespaces for "pure code being brought in" and "powers granted to me by my container" are being conflated. For the former, you need modules. For the latter, you need some sort of "service registry" or whatever else.
I think that in many cases, conflating these two concepts is appropriate. For example, if I import jQuery to add some simple bit of functionality to my homepage, I almost certainly want to give it access to all of the things I have access to. This is the common case that our strawman tries to make simple. However, in other contexts a service registry may be more appropriate, and that can be built on top of modules, using either the dynamic loading features, or simple lexical scope with functions and objects.
Another point worth highlighting: simple and common (and safe in their
own terms) chores should be easy to write; more complex use-cases
should be possible, while obviously less simple to express. Doing it
the other way 'round, requiring the more complex forms to do simple
thigns, overcomplicates the language.
The pitfall of having to do everything today on extant browsers using
first class functions and objects (and code generators) should not
dictate complexity for all use-cases in the integrated module system.
Suppose - maybe unlikely - the client platform webcam is exposed to ECMAScript in a HTML5 webapp. How would this be accessed in a module system:
import modwebcam; // what is this? native code? do i have access to it? via a container/context/global object? import JSON; // is this native code?
How modules, contexts (global objects) and powerful platform resources/objects/capabilities interact is interesting. At the moment platform objects are placed in the global object. In Ihab's proposal access to platform objects are parcelled out via 'parametized modules'. Modules are explicity passed resources at instantiation (i think).
Two type of module: impure modules, which access platform modules (eg xhr, dom, webcam) VS pure modules (in memory calculations).
On Feb 1, 2010, at 9:55 AM, Kevin Curtis wrote:
Suppose - maybe unlikely - the client platform webcam is exposed to
ECMAScript in a HTML5 webapp.
The whole issue of webcam API security is important, but not in es- discuss, and not tied to module systems.
I dread the w3c pushing an access-control security mechanism for
webcam access, because confused deputy attacks and remote webcam-
jacking are guaranteed. One wants not only the webcam itself, but also
the images captured using it, to be protected by proxies (strawman:proxies
) and other new tools used to implement membranes that pervasively
mediate access. For a start!
More to say on this topic, but not here.
Anyway, modules as separately developed/deployed source units do not
really come into play here.
How would this be accessed in a module system:
import modwebcam; // what is this? native code? do i have access to
it? via a container/context/global object?
Why does native code matter?
In either module system approach, first-class or second-class, nothing
prevents a module from conveying authority, which may be unwanted or
even hazardous. Zero-authority modules and a service registry are as
far as I can tell advisory, not mandatory (but I haven't seen the full
proposal yet).
Programmers can do all sorts of powerful things with objects already.
Module systems as proposed and discussed do not add or subtract
authority. These are separate issues.
import JSON; // is this native code?
Why does native code matter here?
How modules, contexts (global objects) and powerful platform
resources/objects/capabilities interact is interesting.
Yes, the Context separation Kris presented last week, which Dave
advocated here on the list, is a good one. We need a strawman proposal
on it.
At the moment platform objects are placed in the global object. In
Ihab's proposal access to platform objects are parcelled out via
'parametized modules'. Modules are explicity passed resources at
instantiation (i think).Two type of module: impure modules, which access platform modules
(eg xhr, dom, webcam) VS pure modules (in memory calculations).
This "impure" or "platform" vs. "pure" distinction is not enforced, or
enforceable, under any proposal presented so far. It also has nothing
to do with second- vs. first-class modules, AFAICT.
It's important to note again that the second-class proposal relies on
lexical scope for isolation. Importing all exports imports static
bindings with error on conflict, and you can selectively import (with
renaming) if you want to control bindings in your own lexical scope.
On Mon, Feb 1, 2010 at 9:21 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
That's only the case if X, Y, and Z provide access to their internal state via the bindings they export.
The problem is not defending the integrity of X, Y and Z. The problem is this:
import X as ...; import Y as ...;
function f1() { /* operate on the authorities of 'X' / } function f2() { / operate on the authorities of 'Y' */ }
In this fashion, I cannot limit, via lexical scope, that f1 operates on X but not Y, and that f2 operates on Y but not X.
It has been my experience that most of the isolation needed between modules can be accomplished by lexical scope.
See above.
... if I import jQuery to add some simple bit of functionality to my homepage, I almost certainly want to give it access to all of the things I have access to.
"Giving <something> access to all of the things I have access to" is
the problem that leads to excess authority, the separation of designation from authorization assuming ambient authority, and confused deputy vulnerabilities.
Put another way, in the following:
there is the goal:
"Support a statically verifiable, object-capability secure subset."
How does your proposal subset down this way?
Ihab
On Mon, Feb 1, 2010 at 1:08 PM, Brendan Eich <brendan at mozilla.com> wrote:
It's important to note again that the second-class proposal relies on lexical scope for isolation. Importing all exports imports static bindings with error on conflict, and you can selectively import (with renaming) if you want to control bindings in your own lexical scope.
See my latest response to Sam.
Ihab
-----Original Message----- From: ihab.awad at gmail.com ...
The problem is not defending the integrity of X, Y and Z. The problem is this:
import X as ...; import Y as ...;
function f1() { /* operate on the authorities of 'X' / } function f2() { / operate on the authorities of 'Y' */ }
In this fashion, I cannot limit, via lexical scope, that f1 operates on X but not Y, and that f2 operates on Y but not X.
But the above four lines are all in the same module, that is in the same unit of source code. As the author of this module you control what you put into the bodies of both f1 and f2 so you are perfectly free to restrict what you put into either. If you don't trust yourself, then create separate modules for f1 and f2 that only import X or Y and not both. ...
there is the goal:
"Support a statically verifiable, object-capability secure subset."
How does your proposal subset down this way?
It seems to me that the 2nd class module proposal is all about:
"Be a better language for writing: complex applications; libraries (possibly including the DOM) shared by those applications;"
rather than an "object-capability secure subset."
They are about dividing up the source code of complex application and library into manageable units that easily integrate together in a way that avoids unintended naming clashes. This is completely orthogonal to objects as capabilities or any other dynamic properties of programs.
On Mon, Feb 1, 2010 at 4:48 PM, <ihab.awad at gmail.com> wrote:
On Mon, Feb 1, 2010 at 9:21 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
That's only the case if X, Y, and Z provide access to their internal state via the bindings they export.
The problem is not defending the integrity of X, Y and Z. The problem is this:
import X as ...; import Y as ...;
function f1() { /* operate on the authorities of 'X' / } function f2() { / operate on the authorities of 'Y' */ }
In this fashion, I cannot limit, via lexical scope, that f1 operates on X but not Y, and that f2 operates on Y but not X.
I'm confused about the problem here. It seems like this is the case in both your proposal and Kris' proposal as well:
In Emaker style:
var x = (import 'x')({}); var y = (import 'y')({});
function f1() { ... } function f2() { ... }
or in CommonJS style (even more directly):
var x = require('x'); var y = require('y');
function f1() { ... } function f2() { ... }
In all three systems, it seems like the right thing is to explicitly
parameterize f1' over an object providing the capabilities of X, and
f2' similarly. Perhaps I've misunderstood how this would work in
Emaker-style?
It has been my experience that most of the isolation needed between modules can be accomplished by lexical scope.
See above.
Here is where I think there's an important difference between the
style of isolation you want for different modules in the same software
product (maybe both written by me, but in separate areas of
functionality) and the style you want for really untrusted code, such
as code for ads on the web, or the program being edited in an editor,
or student programs in an auto-grader. For the former, we could
simply divide the module into two, one with f1' and the other with
f2'. For the latter, something much stronger is necessary - the code
shouldn't even be able to name the functionality that it shouldn't
get. But supporting the latter requires the sort of explicit
configuration that's a very hard sell in the general case, especially
for many of the use cases of EcmaScript.
... if I import jQuery to add some simple bit of functionality to my homepage, I almost certainly want to give it access to all of the things I have access to.
"Giving <something> access to all of the things I have access to" is the problem that leads to excess authority, the separation of designation from authorization assuming ambient authority, and confused deputy vulnerabilities
Unfortunately, it's also really convenient. In an Emaker-style language, libraries might develop the convention that everything takes a free variable called 'dom', which gives access to the whole DOM. That wouldn't be a good way to write secure code, but it would take less thinking on the part of authors. There's nothing that's going to force people to use the system in a secure way.
there is the goal:
"Support a statically verifiable, object-capability secure subset."
How does your proposal subset down this way?
The same way that other scope and name management tools, such as `function', do. The strawman provides a way to program without having the mutable global object on the scope chain, and with lexical scope. Those both seem perfectly compatible with the subset. It also provides a name resolution mechanism, which resolves names to bits of code - this is common to all of the proposals. As in the other proposals, there needs to be a mechanism for restricting this mechanism, and I think we all have something similar in mind for how that should work, based on what Kris described.
On Feb 1, 2010, at 1:48 PM, ihab.awad at gmail.com wrote:
On Mon, Feb 1, 2010 at 9:21 AM, Sam Tobin-Hochstadt
<samth at ccs.neu.edu> wrote:That's only the case if X, Y, and Z provide access to their internal state via the bindings they export.
The problem is not defending the integrity of X, Y and Z. The
problem is this:import X as ...;
In the proposal, the ... is the name of a first-class object you can
use to reference exports from X. If X exports f1 this form does not
bind f1 lexically in the importing scope.
So
import "X" as X; import "Y" as Y; X.f1(); Y.f2();
would be a minimal example of the importing code, if I am reading
between the lines correctly.
function f1() { /* operate on the authorities of 'X' / } function f2() { / operate on the authorities of 'Y' */ }
In this fashion, I cannot limit, via lexical scope, that f1 operates on X but not Y, and that f2 operates on Y but not X.
What exactly do you mean by "operates on X but not Y"?
In the proposal, and reading between your example's lines slightly
(again I'm assuming X exports f1, Y exports f2), f1 is statically
scoped to X, f2 is statically scoped to Y.
So there is absolutely a lexical scope barrier between X and Y that
prevents f1 from operating on Y's unexported variables, and vice versa
for f2 and X.
... if I import jQuery to add some simple bit of functionality to
my homepage, I almost certainly want to give it access to all of the things I
have access to."Giving <something> access to all of the things I have access to" is the problem that leads to excess authority, the separation of designation from authorization assuming ambient authority, and confused deputy vulnerabilities.
No, allowing an importer to bind static variables to the exports of
a given module does not create ambient authority. It is an explicit
action by the importer based on a module identifier resolved to a
specific module. There's no ambient. The imported module exports
exactly f1, and nothing else.
People do this all the time, it is often a necessary step when getting
started prototyping with modules (say, when you create both importer
and exporter at once), and it is often the end-point too.
If the module identifier and resolution machinery you've wired up make
the importer vulnerable to more names being added over time, then
"don't do that":
-
either use a specific external-linkage identifier (often to your
jquery.js, which you copied from a fixed version once and never
updated), -
or use the other forms of import to get only the bindings you want,
or a first-class reflection of the module from which to access just
the exports you want.
Nothing here creates ambient authority as I understand that phrase.
Put another way, in the following:
there is the goal:
"Support a statically verifiable, object-capability secure subset."
How does your proposal subset down this way?
Far be it from me to speak for Sam, but I was involved in codifying
this Harmony goal, and I see no conflict at all so far.
But let's not put the cart before the horse. The subset is not the
set. If the subset excludes the import "X"; form, allowing only import
"X": f1; or import "X" as X; forms, then we are done. First, though,
let's get back to what you meant above by "operates on X but not Y".
Again, objects have authority and functions operate on entities. It's
not the case that f1 can get at Y's lexical scope, or f2 at X's. This
is key to the proposal.
On Feb 1, 2010, at 2:20 PM, Brendan Eich wrote:
The problem is not defending the integrity of X, Y and Z. The
problem is this:import X as ...;
In the proposal, the ... is the name of a first-class object you can
use to reference exports from X. If X exports f1 this form does
not bind f1 lexically in the importing scope.So
import "X" as X; import "Y" as Y; X.f1(); Y.f2();
would be a minimal example of the importing code, if I am reading
between the lines correctly. [...] In the proposal, and reading between your example's lines slightly
(again I'm assuming X exports f1, Y exports f2), f1 is statically
scoped to X, f2 is statically scoped to Y.
I clearly read between the lines differently from Allen and Sam -- but
in my defense, their reading did not make any sense to me :-P.
If f1 and f2 are in the importing code, then there is no salient
difference among the module system proposals.
And anyway, if you want to isolate things in your own code you can
always wrap those things in modules as Allen said.
Perhaps the ambient authority issue to do with import "X"; (not import
"X" as X; or import "X": f1) is the issue after all.
On Mon, Feb 1, 2010 at 9:08 PM, Brendan Eich <brendan at mozilla.com> wrote:
In either module system approach, first-class or second-class, nothing
prevents a module from conveying authority, which may be unwanted or even hazardous. Zero-authority modules and a service registry are as far as I can tell advisory, not mandatory (but I haven't seen the full proposal yet).
A service registry sounds interesting. Is there a proposal out there (in this group or elsewhere) dealing with how/where authority to platform objects or object capabilities are dished out.
On Feb 1, 2010, at 2:46 PM, Kevin Curtis wrote:
A service registry sounds interesting. Is there a proposal out there
(in this group or elsewhere) dealing with how/where authority to
platform objects or object capabilities are dished out.
Ihab talked about it on the list:
old.nabble.com/Native-modules-td27231395.html
I don't see it in the wiki yet.
Sounds good. A Context is configured with the objects (eg dom, xhr) that the developer wants to make accessible in the Context. These objects are bound to an outer lexical frame which all modules imported into the Context can access. Contexts are the means by which access to platform resources are mediated. (Or is that wrong. Hey - i'll wait for the strawman :).
In terms of mechanism, that's not exactly what I had in mind, but in terms of purpose that's the rough idea (specifically: "contexts are the means by which access to platform resources are mediated" -- yes). Creating new contexts would make it possible to restrict what modules can be imported, so you could create "pure" execution contexts in which you could evaluate code that would not have access to anything with interesting authority. This would of course be host-dependent.
On Mon, Feb 1, 2010 at 11:13 PM, David Herman <dherman at mozilla.com> wrote:
In terms of mechanism, that's not exactly what I had in mind, but in terms of purpose that's the rough idea (specifically: "contexts are the means by which access to platform resources are mediated" -- yes). Creating new contexts would make it possible to restrict what modules can be imported, so you could create "pure" execution contexts in which you could evaluate code that would not have access to anything with interesting authority. This would of course be host-dependent.
Dave
Understood. Just a final point - modules that are imported with 'import' can be native modules? That is modules that are implemented in C++.
// compiler tries each in order import ['JSON', 'json.org/modules/json2.js'] as JSON;
In the above case 'JSON' is a native module implemented in C++. If it doesn't exist a pure ES solution is used. From the end user point of view it is no matter if a module is implemented natively or via pure ES. And it is possible, to configure a new context which will deny access to the native JSON module to code and imported modules within this context - if the developer desires.
Also, a general point. There has been discussion re ArrayBuffers. Would 'native modules' be a way to present this functionality to ECMAScript. As an alternative to putting them in the global object/namespace.
import ModArrayBuffer; // imports ArrayBuffer, Int32Array, Int16Array etc
buf = new ArrayBuffer(100); // create 100-byte ArrayBuffer var ints = new Int32Array(buf); // view that buffer as 32-bit integers var fourShorts = new Int16Array(buf, 20, 4); // Starting at byte 20 in the buffer, create a 16-bit
// end
On Mon, Feb 1, 2010 at 8:28 PM, Kevin Curtis <kevinc1846 at googlemail.com> wrote:
On Mon, Feb 1, 2010 at 11:13 PM, David Herman <dherman at mozilla.com> wrote:
In terms of mechanism, that's not exactly what I had in mind, but in terms of purpose that's the rough idea (specifically: "contexts are the means by which access to platform resources are mediated" -- yes). Creating new contexts would make it possible to restrict what modules can be imported, so you could create "pure" execution contexts in which you could evaluate code that would not have access to anything with interesting authority. This would of course be host-dependent.
Dave
Understood. Just a final point - modules that are imported with 'import' can be native modules? That is modules that are implemented in C++.
Right. The host environment could provide some set of modules, along with some naming scheme for referring to them.
// compiler tries each in order import ['JSON', 'json.org/modules/json2.js'] as JSON; In the above case 'JSON' is a native module implemented in C++. If it doesn't exist a pure ES solution is used. From the end user point of view it is no matter if a module is implemented natively or via pure ES.
In the strawman, 'JSON' could be implemented in pure ES - as long as it's declared in a place where the import statement can see it (in the same Program, say). In a modern browser, 'JSON' might be provided natively, but the strawman doesn't commit to that.
And it is possible, to configure a new context which will deny access to the native JSON module to code and imported modules within this context - if the developer desires.
That's the idea (due to Kris Kowal).
Also, a general point. There has been discussion re ArrayBuffers. Would 'native modules' be a way to present this functionality to ECMAScript. As an alternative to putting them in the global object/namespace. import ModArrayBuffer; // imports ArrayBuffer, Int32Array, Int16Array etc buf = new ArrayBuffer(100); // create 100-byte ArrayBuffer var ints = new Int32Array(buf); // view that buffer as 32-bit integers var fourShorts = new Int16Array(buf, 20, 4); // Starting at byte 20 in the buffer, create a 16-bit // end
Whatever we come up with for modules would be a good way to structure all of the potential additions in Harmony - ArrayBuffers, Proxies, etc.
Thanks. I can see how it hangs together now.
Whatever we come up with for modules would be a good way to structure
all of the potential additions in Harmony - ArrayBuffers, Proxies, etc.
sam th samth at ccs.neu.edu
Also for new HTML5 functionality. Furthermore, browser vendors could pioneer new features without worrying too much about namespace clashes. import moz.stuff import goog.stuff import apple.morestuff
And ideally de facto and actual standards emerge from this pioneering. import ['std.stuff', 'goog.stuff'] as stuff; // use vendor specific module if standard version not available.
On Mon, Feb 1, 2010 at 10:09 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
If you don't trust yourself, then create separate modules for f1 and f2 that only import X or Y and not both.
Ok, that's a good solution.
It seems to me that the 2nd class module proposal is all about:
"Be a better language for writing: complex applications; libraries (possibly including the DOM) shared by those applications;"
rather than an "object-capability secure subset."
If a module proposal were unable to support the latter, then this should be a serious disadvantage, given that an ocap secure subset is a stated goal of ES-Harmony.
If the proposal's only way of supporting an ocap secure subset is to say, "don't use modules", or some other exhortation to wade into the Turing tarpit, then that should also be a serious disadvantage.
Let me illustrate by describing how, as I see it, I would have to program with the 2nd class [*] proposal in order to partition authority in a fine-grained manner:
-
I would have to create a new Context each time I wish to create a subset of my running objects having only a clear subset of my authority.
-
I would not have the benefits of the static analysis, so I would have to use some function that wraps around the proposed loadModule function. These benefits I would relinquish would be:
(a) My code would no longer have the performance benefits of having several modules linked and sent to the use site together by some assembly tool; and
(b) My code would be complicated by the need or asynchronous access to the results of the loaded modules.
This means that:
var point = (import 'point')({ x: 3, y: 4 })
/* export service based on 'point' */
becomes:
/* declare promise to service based on 'point' */
makeNewContext('point', { x: 3, y: 4 }, function(thePoint) {
/* fulfil promise to service based on 'point' */
});
/* export promise to service based on 'point' */
They are about dividing up the source code of complex application and library into manageable units that easily integrate together in a way that avoids unintended naming clashes. This is completely orthogonal to objects as capabilities or any other dynamic properties of programs.
The static properties of programs are the way we arrange to reason about the dynamic properties of the object graph induced by the programs. As I note above, in patterns of use pursuant to the ocap paradigm, the lexical structure is definitely not by any means orthogonal to the dynamic flow of authority.
Ihab
I forgot to include my footnote!
On Tue, Feb 2, 2010 at 2:39 AM, <ihab.awad at gmail.com> wrote:
... the 2nd class [*] proposal ...
[*] - The name "2nd class" is something of a convenience for the moment. Whether modules are 1st or 2nd class is a downstream thing we can talk about later; there are both 1st and 2nd class ways to build a module system that would satisfy the goals of an ocap secure SES (and I hint towards that in my own proposal, without fully joining the dots). But for now, let's get the sharing module figured out.
Ihab
2010/2/2 David Herman <dherman at mozilla.com>
Hi Vassily, thanks for the feedback.
It should be
<script type="harmony">
// import everything as Math import "Math";
alert("2π = " + Math.sum(Math.pi, Math.pi));
</script>
This is already possible with the `import "Math" as Math' form (which incidentally can easily be compiled to be exactly as efficient). Leaving the "as Math" part implicit doesn't work if the module specifier is not syntactically an identifier:
import "@#$!"; @#$!.mumble("grunch")
We already have "with" for polluting local namespace, and short syntax for such polluting doesn't feel right.
That's an inappropriate comparison, for two critical reasons. First: `with' dynamically changes the environment, so it destroys lexical scope, whereas when you import everything from a second-class module, it is still possible to know statically what bindings are in scope. Second: there's no contention, since it's a static error to import two modules that bind the same name. So conflicts are ruled out, and there's no ambiguity in a valid program.
If one module can extend its API and break code that depends on it, how does this proposal solve the namespacing problem?
On Mon, Feb 1, 2010 at 10:20 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Feb 1, 2010, at 1:48 PM, ihab.awad at gmail.com wrote:
"Giving <something> access to all of the things I have access to" is the problem that leads to excess authority, the separation of designation from authorization assuming ambient authority, and confused deputy vulnerabilities.
No, allowing an importer to bind static variables to the exports of a given module does not create ambient authority.
I will try to explain what I meant by ambient authority in this case.
Assume I am the code of a module, by the 2nd-class proposal, with access to some module -- call it "fs". This module, being stateful, contains authority, such as read/write authority to some portion of the filesystem in my installation.
I now wish to construct an object, running some code, which has no access to the filesystem. I wish to do so because either or both of the following is true: (a) this object will eventually contain untrusted code; or (b) this object does not need access to the filesystem, and I wish to confine it based on least authority so as to better reason about the flow of authority in my system.
Because any object I create in my Context is capable of gaining access to the filesystem simply by uttering "import fs", the "fs" authority is ambient in my Context.
(This reasoning also applies to the case where "fs" has little powers, but I wish to construct two objects which are unable to establish a mutual communication channel simply by uttering "import fs".)
These patterns are precisely what is meant when we talk about an object capability language. They are precisely what must be supported by an object capability subset of an existing language.
As I noted in reply to Allen, the 2nd class proposal simply relegates these patterns to the Turing tarpit.
It is worthwhile to ask whether support for these patterns belongs in a module system. The 2nd class proposal claims that they are orthogonal. If the tarpit is acceptably the only place where object capability programming may happen, they are. Otherwise, they are simply pervasive concerns that should inform every part of our system design, much as do other concerns such as ease of use, clarity, and contribution to runtime performance.
The pervasive concern of the need to program in an ocap manner informs the module discussion by noting that, where I load a module, I am delegating to some other code. I wish to modularize my code by reasoning in the large about the effects of the module I am loading. Part of reasoning in the large about this code is reasoning about the flow of authority that it may manipulate. This code to which I delegate is "foreign" to varying degrees: it may be code I wrote 5 minutes ago, or it may be a component from a completely untrusted author. This quality of foreign-ness is on a continuum; 2 extreme solutions (easy and loose; difficult and secure) are not adequate to describe the richness of cases I will encounter. I will delegate authority to that code in relation to both its need for authority and my ability to make myself vulnerable to that code -- including (and this is a key point) its unintended consequences and vulnerabilities.
- either use a specific external-linkage identifier (often to your jquery.js, which you copied from a fixed version once and never updated),
... which copy, while a fixed known, contains known unknowns: the vulnerabilities and unintended consequences I noted earlier.
- or use the other forms of import to get only the bindings you want, or a first-class reflection of the module from which to access just the exports you want.
... which brings us to the tarpit.
It's not the case that f1 can get at Y's lexical scope, or f2 at X's. This is key to the proposal.
Yep, we both agree on this point.
Ihab
Another clarification:
On Tue, Feb 2, 2010 at 3:17 AM, <ihab.awad at gmail.com> wrote:
Assume I am the code of a module, by the 2nd-class proposal ...
In this example, what I'm saying is that "I" am coding a module that says:
import "fs" as fs; /* now create one or more objects with limited authority */
Ihab
On Mon, Feb 1, 2010 at 10:09 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
import X as ...; import Y as ...;
function f1() { /* operate on the authorities of 'X' / } function f2() { / operate on the authorities of 'Y' */ }
In this fashion, I cannot limit, via lexical scope, that f1 operates on X but not Y, and that f2 operates on Y but not X.
But the above four lines are all in the same module, that is in the same unit of source code.
Yes, but there's a difference. Consider the following:
import X as x; import Y as y; function f1() { /* deeply nested structure / } function f2() { / deeply nested structure */ }
It is very difficult to tell whether f1() limits its authority to using X, and f2() to using Y. Now consider:
import X as x; import Y as y; (import 'f1')({ x: x }); (import 'f2')({ y: y });
This is much clearer. Alternatively:
import X as x; import Y as y; module f1(x) { /* deeply nested structure / } module f1(y) { / deeply nested structure */ } f1({ x: x }); f2({ y: y });
achieves the same purpose, given that the 'module' keyword cuts the lexical scope.
Absent syntactic bikeshedding (for which there will be plenty of time :) ...) what I'm trying to show here is that a good module system helps you divide up your lexical scope.
Ihab
On Tue, Feb 2, 2010 at 9:20 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
I'm confused about the problem here.
See previous; hope that clarifies.
Here is where I think there's an important difference between the style of isolation you want for different modules in the same software product (maybe both written by me, but in separate areas of functionality) and the style you want for really untrusted code, such as code for ads on the web, or the program being edited in an editor, or student programs in an auto-grader.
As I argued previously, there does not exist a dichotomy of two extremes. There is a whole spectrum of possibilities, driven by both security and software engineering concerns.
For the latter, something much stronger is necessary - the code shouldn't even be able to name the functionality that it shouldn't get. But supporting the latter requires the sort of explicit configuration that's a very hard sell in the general case, especially for many of the use cases of EcmaScript.
Fortunately, you provide a really good solution later on ...
Unfortunately, it's also really convenient. In an Emaker-style language, libraries might develop the convention that everything takes a free variable called 'dom', which gives access to the whole DOM. That wouldn't be a good way to write secure code, but it would take less thinking on the part of authors. There's nothing that's going to force people to use the system in a secure way.
... and there it is! Libraries wishing to pass lots of authority may establish conventions whereby they arrange to do so. Libraries that need to pass less authority may also do so. And, crucially, a library that accepts the "dom" variable is known to have access to nothing else; nothing that could cause surprise later on.
"Support a statically verifiable, object-capability secure subset." How does your proposal subset down this way?
The same way that other scope and name management tools, such as `function', do. The strawman provides a way to program without having the mutable global object on the scope chain, and with lexical scope.
Lexical scope whereby it is possible for any component downstream to "wormhole" through the authority graph by uttering the name of a module. In other words, with the surface syntax of lexical scope but foregoing its advantages in establishing division of authority (for security) and dependencies (for software engineering).
... It also provides a name resolution mechanism, which resolves names to bits of code - this is common to all of the proposals.
Crucially, the 2nd class proposal resolves names to bits of code and shared mutable state with authority to affect the outside world.
Ihab
On Mon, Feb 1, 2010 at 10:46 PM, Kevin Curtis <kevinc1846 at googlemail.com> wrote:
A service registry sounds interesting. Is there a proposal out there (in this group or elsewhere) dealing with how/where authority to platform objects or object capabilities are dished out.
I made up a strawman here:
Note the "variant" as I am not wedded to the specifics of how the named objects are attached. The most important properties to agree upon are the role of this object: that it serves as a registry for varied stuff; that it is managed under the assumption of cooperation; and that it is intended to convey system-provided material, not to act as a central point of registration for user-provided components. As for the latter, there may indeed be a need for such a thing, but this isn't it.
Ihab
On Mon, Feb 1, 2010 at 11:01 PM, Brendan Eich <brendan at mozilla.com> wrote:
Ihab talked about it on the list: old.nabble.com/Native-modules-td27231395.html
Strawman sent.
Ihab
On Mon, Feb 1, 2010 at 11:13 PM, David Herman <dherman at mozilla.com> wrote:
... (specifically: "contexts are the means by which access to platform resources are mediated" -- yes). Creating new contexts would make it possible to restrict what modules can be imported, so you could create "pure" execution contexts in which you could evaluate code that would not have access to anything with interesting authority.
As I noted before, contexts are fine and dandy, but creating one of them is a Big Deal [TM]. This means that they will not be used to devolve authority at fine grain. In fact, given the tarpit method of creating a new context, and from a language usability perspective, they arguably cannot be used to devolve authority at fine grain.
This would of course be host-dependent.
Not sure what you mean by that ... perhaps you mean "configurable by the programmer"? If so yes, but we still disagree on the mechanisms.
Ihab
On Tue, Feb 2, 2010 at 1:28 AM, Kevin Curtis <kevinc1846 at googlemail.com> wrote:
Just a final point - modules that are imported with 'import' can be native modules?
Yes. As long as the module does not provide powers, i.e., it is just equivalent to code, it can be provided ambiently as a module.
There has been discussion re ArrayBuffers. Would 'native modules' be a way to present this functionality to ECMAScript.
To the extent that they are just powerless code, yes.
Ihab
On Tue, Feb 2, 2010 at 1:39 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
Whatever we come up with for modules would be a good way to structure all of the potential additions in Harmony - ArrayBuffers, Proxies, etc.
Yes: for powerless code, that I do agree with.
Ihab
On Feb 1, 2010, at 7:17 PM, ihab.awad at gmail.com wrote:
Because any object I create in my Context is capable of gaining access to the filesystem simply by uttering "import fs", the "fs" authority is ambient in my Context.
This is simply not the case. The Context proposal needs to be written
still, but we have already discussed several times how a Context can
have its own module id resolver, which can deny "fs" and anything else
to the would-be importer.
These patterns are precisely what is meant when we talk about an object capability language. They are precisely what must be supported by an object capability subset of an existing language.
JS is not going to become an objcap language. The Harmony goal of a
statically verifiable secure subset is about a subset, not just
leaving out legacy features we can't get rid of, but possibly also new
features added for convenience and usability.
The full language is not required to have objcap-only new features.
There's a related debate in the committee about new sources of non-
determinism, which is ongoing.
But the point of order I am raising is this: modules for Harmony, and
other additions to the full set (not the secure subset) are not
required to be object capability language features or patterns, and
only objcap features.
On Tue, Feb 2, 2010 at 10:41 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Feb 1, 2010, at 7:17 PM, ihab.awad at gmail.com wrote:
Because any object I create in my Context is capable of gaining access to the filesystem simply by uttering "import fs", the "fs" authority is ambient in my Context.
This is simply not the case. The Context proposal needs to be written still, but we have already discussed several times how a Context can have its own module id resolver, which can deny "fs" and anything else to the would-be importer.
These patterns are precisely what is meant when we talk about an object capability language. They are precisely what must be supported by an object capability subset of an existing language.
JS is not going to become an objcap language. The Harmony goal of a statically verifiable secure subset is about a subset, not just leaving out legacy features we can't get rid of, but possibly also new features added for convenience and usability.
The full language is not required to have objcap-only new features. There's a related debate in the committee about new sources of non-determinism, which is ongoing.
But the point of order I am raising is this: modules for Harmony, and other additions to the full set (not the secure subset) are not required to be object capability language features or patterns, and only objcap features.
I'm only now catching up on this thread, so I'm responding out of order to this one first, and perhaps without sufficient context.
Based on my skimming of the thread to date, I don't think anyone disagrees with your abstract point. We all agree that only a statically verifiable subset of Harmony needs to be an ocap language. But this should be a usable ocap language, not gratuitously lacking any features from full Harmony that could have been provided safely, merely because Harmony unnecessarily chose to provide them in an insecurable manner.
In particular, it would be bizarre for Harmony to have two distinct and disjoint module systems, A and B, simply because module system A was unnecessarily inappropriate for the ocap subset. Module system B, suitable for the ocap subset, must also then be in the full language, in order for SES to be a subset. Unless of course B is itself a subset of A.
Since SES needs an ocap-compatible module system, and since this module system must be within a subset of Harmony, it makes more sense to me to start with the more constrained problem: Let's design a module system B adequate for the needs of both SES and Harmony. Once we understand the shape of that, then we can reexamine whether Harmony still needs a second insecurable module system, or merely an insecure superset of the secure module system.
On Feb 2, 2010, at 10:58 AM, Mark S. Miller wrote:
In particular, it would be bizarre for Harmony to have two distinct
and disjoint module systems, A and B, simply because module system A
was unnecessarily inappropriate for the ocap subset.
No one is proposing this. It would be bizarre. It's a false dilemma.
On the other hand, the second class "simple modules" proposal, plus
the impending Context proposal, allows A and subset-A, as far as I can
tell. But I'll let others say more.
Since SES needs an ocap-compatible module system, and since this
module system must be within a subset of Harmony, it makes more
sense to me to start with the more constrained problem: Let's design
a module system B adequate for the needs of both SES and Harmony.
Once we understand the shape of that, then we can reexamine whether
Harmony still needs a second insecurable module system, or merely an
insecure superset of the secure module system.
Sorry, this is too biased and path-dependent a design approach. The
space we are "searching" is large. We need to consider alternatives at
both layers, and where possible avoid too much layering.
Layering is a problem, not a solution.
But this should be a usable ocap language, not gratuitously lacking any features from full Harmony that could have been provided safely, merely because Harmony unnecessarily chose to provide them in an insecurable manner.
That's a bit hyperbolic; no one's proposing an "insecurable" system. Our proposal strives to let simplicity and conciseness outweigh isolation in the common case, and yet still make isolation possible. Within that isolation there should plenty of room for building secure subsets.
For example, much of the first-class module strawmen have attempted to build off a secure eval; simple modules + isolation would essentially provide that. (If that's not obvious, it's entirely our fault, since we haven't written up anything on isolation yet.)
In particular, it would be bizarre for Harmony to have two distinct and disjoint module systems, A and B, simply because module system A was unnecessarily inappropriate for the ocap subset.
Well, they can be addressing different concerns, especially when they have pretty dramatic differences. But more to the point, starting with the goal of supporting all the features needed for an objcap subset is needlessly tying our hands behind our backs. Nobody's trying to jeopardize objcap subsetting, but we also can't let objcap goals eclipse others.
On Tue, Feb 2, 2010 at 6:41 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Feb 1, 2010, at 7:17 PM, ihab.awad at gmail.com wrote: Because any object I create in my Context is capable of gaining access to the filesystem simply by uttering "import fs", the "fs" authority is ambient in my Context.
This is simply not the case. The Context proposal needs to be written still, but we have already discussed several times how a Context can have its own module id resolver, which can deny "fs" and anything else to the would-be importer.
I'm confused.
"I" am a piece of code. I exist in a Context wherein module instances are ambiently shared. Within that context, "fs" resolves to the filesystem module. I import some other module. Can I prevent that module from importing "fs"? Is access to imports importer-subjective?
I think the point you are making is that it is possible to create a new Context that has no, or attenuated, access to the "fs" module. That's fine, but then we move to the arguments about the Turing tarpit presented to programmers wishing to work in a fine-grained secure manner.
Ihab
On Tue, Feb 2, 2010 at 7:24 PM, Brendan Eich <brendan at mozilla.com> wrote:
On the other hand, the second class "simple modules" proposal, plus the impending Context proposal, allows A and subset-A, as far as I can tell.
Yes, it allows subset-A by the creation of new Contexts.
The result is not a "capability language" as commonly understood; the language constructs by themselves are not prima facie usable to implement ocaps. The result is rather more akin to a capability operating system, in which somewhat coarse-grained units of trust share information freely among themselves, while having limited authority at their boundaries.
If TC39 wishes to construct such a system in lieu of a capability language in SES, the committee needs to achieve concensus on this issue first -- and update the wiki! -- before commissioning module proposals.
Ihab
On Tue, Feb 2, 2010 at 12:19 AM, <ihab.awad at gmail.com> wrote:
See previous; hope that clarifies.
Ihab -
[responding to a bunch of messages together]
I think I now have a better grasp on your concerns, so I hope I can give a better answer. It seems like the primary concerns are twofold:
- Since modules are stateful, how can a secure subset avoid inadvertently creating communication channels between modules.
- Since the module system provides a global way to name modules, any module can name powerful, stateful, modules provided by the host environment.
Is that a reasonable characterization?
I'll try to address each of these points in turn.
First, there's a simple subset of module system which (I think) meets ocap criteria. Simply consider only modules which have no 'top-level' state, that is, that do not mutate any state that is created by simply executing the module. For example, the counter module example Dave gave (now on the wiki) doesn't have any top-level state. Programming in this subset is not as convenient, but it certainly avoids any shared state or inadvertent communication. And it allows the same patterns that are available in Emaker-style modules. I don't think that this is devolving into the Turing Tarpit.
Unfortunately, that story only works for pure ES code, and doesn't say anything about how to program in an ocap style with stateful modules provided by the host environment. Further, even if the host only provided functions that would be invoked, a module in ES could still invoke that function and thus allow access to the powerful host object.
The primary solution here would be to set up a restricted Context which restricted access to particular stateful modules (this of course works for all stateful modules). You've described this as a heavyweight operation, but I hope we can make it convenient enough not to be a barrier to use. You've also said that you don't think it can be used for fine-grained authority, but I don't see why not.
Further, if SES is the subset described earlier, with access only to stateless modules, along with stateless versions of the host modules, then it seems possible to program in a perfectly ocap style in this language.
Does that make sense?
sam th samth at ccs.neu.edu
On Feb 2, 2010, at 12:05 PM, ihab.awad at gmail.com wrote:
I think the point you are making is that it is possible to create a new Context that has no, or attenuated, access to the "fs" module. That's fine, but then we move to the arguments about the Turing tarpit presented to programmers wishing to work in a fine-grained secure manner.
No, we do not fall into the tarpit.
The subset language can provide only the authority-free modules (Sam
just replied saying this too). It simply can't be the full language,
or the language embedded in <script type="application/javascript"> --
but it could be the language embedded in <script type="application/ ecmascript;version=secure"> or ...application/ses or whatever.
The totalizing style of arguing whereby if you don't start from ocap
primitives and only those primitives, you cannot reach you desired
outcomes, is not helpful. There are many ways to skin the dinosaur and
not fall into the Turing tarpit.
On Feb 2, 2010, at 12:12 PM, ihab.awad at gmail.com wrote:
On Tue, Feb 2, 2010 at 7:24 PM, Brendan Eich <brendan at mozilla.com>
wrote:On the other hand, the second class "simple modules" proposal, plus
the impending Context proposal, allows A and subset-A, as far as I can
tell.Yes, it allows subset-A by the creation of new Contexts.
The result is not a "capability language" as commonly understood; the language constructs by themselves are not prima facie usable to implement ocaps. The result is rather more akin to a capability operating system, in which somewhat coarse-grained units of trust share information freely among themselves, while having limited authority at their boundaries.
Once again you are changing the terms in the argument -- I was
describing by A the full language, which won't be ocap. But subset-A
(SES) should be ocap, per goal 5.
There is no need to create new Contexts in the SES language. That
would be done when bootstrapping from a full-language <script> tag, e.g.
As I suggested last time, it's conceivable that by extending the
RFC4329 MIME types for JS/ES we could have a <script type="application/
SES"> and no bootstrapping would be required (you'd have to specify
new attributes to inject capabilities, or equivalent).
If TC39 wishes to construct such a system in lieu of a capability language in SES, the committee needs to achieve concensus on this issue first -- and update the wiki! -- before commissioning module proposals.
Sorry, you are mistaken. The consensus is to have a statically
verifiable object capability subset. Not to make the full language use
object capability -- and only ocap -- primitives to erect a module
system for the full language.
On Tue, Feb 2, 2010 at 3:12 PM, <ihab.awad at gmail.com> wrote:
On Tue, Feb 2, 2010 at 7:24 PM, Brendan Eich <brendan at mozilla.com> wrote:
On the other hand, the second class "simple modules" proposal, plus the impending Context proposal, allows A and subset-A, as far as I can tell.
Yes, it allows subset-A by the creation of new Contexts.
New Contexts are necessary only in the case where there are some stateful modules whose access needs to be restricted. Note also that new Contexts are also necessary for programming in an ocap manner in the primordials module system strawman as demoed by Kris at the most recent TC39 meeting. For example, I think he showed an example like this:
var FS = require("fs"); FS.open("myfile");
Here we again have a global namespace, giving access to powerful,
stateful code, accessed by the require' function, which is potentially restricted by using a Context to change the behavior of
require'. (If I've misunderstood what the code Kris presented did,
please let me know.)
The alternative is to pass all state explicitly. This is (I think) what the Emaker-style proposal does. This has the following important drawbacks:
-
It means that the only way to program modularly is to use the stateful-object-passing style. Since this is less convenient for many use cases than simply mutating the global object as in current ES, programmers are less likely to use modules.
-
If we persuade programmers to use modules, they're likely to pass around overly-powerful objects, making reasoning about security harder. A module which takes a single parameter named 'global' and performs all its operations by mutating this object is much harder to reason about than one which imports numerous modules, a few of which are stateful.
The result is not a "capability language" as commonly understood; the language constructs by themselves are not prima facie usable to implement ocaps. The result is rather more akin to a capability operating system, in which somewhat coarse-grained units of trust share information freely among themselves, while having limited authority at their boundaries.
If TC39 wishes to construct such a system in lieu of a capability language in SES, the committee needs to achieve concensus on this issue first -- and update the wiki! -- before commissioning module proposals.
I'm surprised that you don't think this qualifies as a capability language. ES + the simple modules proposal seems to me to be just as much a capability language as the language and module system in Jonathan Rees' dissertation.
On Tue, Feb 2, 2010 at 8:19 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
First, there's a simple subset of module system which (I think) meets ocap criteria. Simply consider only modules which have no 'top-level' state ...
Ok, I think that works. One of my arguments I was going to make downstream was that it would be ok if you required all modules to be transitively immutable. The idea of Contexts wherein all modules are somehow audited to be transitively immutable is an interesting one. Again ... I think that would actually work.
The primary solution here would be to set up a restricted Context which restricted access to particular stateful modules (this of course works for all stateful modules).
Yeah -- well, we can work something out for bootstrapping authority in to Contexts.... Perhaps only a root module would be given some authority? Let's think about it.
You've described this as a heavyweight operation, but I hope we can make it convenient enough not to be a barrier to use. You've also said that you don't think it can be used for fine-grained authority, but I don't see why not.
Well if the Contexts were the only way to divvy up authority, that would be heavyweight. If it were possible to give authority to a root module of a Context, and have all modules imported by that Context be transitively immutable, then I think that achieves the desired effect.
Ihab
On Tue, Feb 2, 2010 at 8:51 PM, <ihab.awad at gmail.com> wrote:
On Tue, Feb 2, 2010 at 8:19 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
The primary solution here would be to set up a restricted Context which restricted access to particular stateful modules (this of course works for all stateful modules).
Yeah -- well, we can work something out for bootstrapping authority in to Contexts.... Perhaps only a root module would be given some authority? Let's think about it.
Actually, here's how to make your proposal work, I think ...
Create a context that allows only transitively immutable modules. Use it to load the root-most module, returning the module instance. This "instance" is actually just a transitively immutable structure.
Now call one or more of the functions of this instance, passing some authority. Each call results in a disconnected object graph with only the authority it was provided.
Ihab
On Tue, Feb 2, 2010 at 11:24 AM, Brendan Eich <brendan at mozilla.com> wrote:
On Feb 2, 2010, at 10:58 AM, Mark S. Miller wrote:
In particular, it would be bizarre for Harmony to have two distinct and
disjoint module systems, A and B, simply because module system A was unnecessarily inappropriate for the ocap subset.
No one is proposing this. It would be bizarre. It's a false dilemma.
Good; this is clarifying. I'm glad no one is proposing that.
On the other hand, the second class "simple modules" proposal, plus the impending Context proposal, allows A and subset-A, as far as I can tell. But I'll let others say more.
Perhaps other's aren't seeing the problem with A that I am seeing: mutable static state. In ocap languages < wiki.erights.org/wiki/Object-capability_languages>, when modules X
and Y both import module Z, that must not by itself provide a communication path between X and Y. This is known as the "mutable static state" problem. If a module instance can contain mutable static state, and if X and Y obtain shared access to the same Z instance merely by importing it in a shared loading context, then communications channels become implicit and not practically deniable.
My apologies for still not having caught up and so possibly still missing some context. But from chatting with Ihab and Tom, it seems the subset of your proposal where an SES loader enforces that it only loads modules which export only transitively immutable values would be ocap-friendly, and would be (perhaps modulo details) within a subset of the 2nd class module proposal.
See gbracha.blogspot.com/2008/02/cutting-out-static.html and <
www.cs.berkeley.edu/~daw/papers/joe-e-ndss10.pdf> for many software
engineering benefits of avoiding mutable static state. As Gilad says,
Given all these downsides, surely there must be some significant upside [to mutable static state], something to trade off against the host of evils mentioned above? Well, not really. It’s just a mistake, hallowed by long tradition. Which is why Newspeak has dispensed with it.
If Gilad is right, then it is a mistake for Harmony as well. I believe it is. But as long as Harmony's module system admits a subset which enforces the absence of static state, I'm happy enough.
Since SES needs an ocap-compatible module system, and since this module
system must be within a subset of Harmony, it makes more sense to me to start with the more constrained problem: Let's design a module system B adequate for the needs of both SES and Harmony. Once we understand the shape of that, then we can reexamine whether Harmony still needs a second insecurable module system, or merely an insecure superset of the secure module system.
Sorry, this is too biased and path-dependent a design approach. The space we are "searching" is large. We need to consider alternatives at both layers, and where possible avoid too much layering.
Layering is a problem, not a solution.
I wholeheartedly agree. If we can design a single module system that can solve all these needs without layering, that would be awesome. I sincerely hope we can.
On Feb 2, 2010, at 1:11 PM, Mark S. Miller wrote:
On the other hand, the second class "simple modules" proposal, plus
the impending Context proposal, allows A and subset-A, as far as I
can tell. But I'll let others say more.Perhaps other's aren't seeing the problem with A that I am seeing:
mutable static state.
No, we all get that. Sam addressed it, others have too (Kevin Curtis,
Ihab of course, and I tried although perhaps I was unclear).
See gbracha.blogspot.com/2008/02/cutting-out-static.html
and www.cs.berkeley.edu/~daw/papers/joe-e-ndss10.pdf for
many software engineering benefits of avoiding mutable static state.
As Gilad says,Given all these downsides, surely there must be some significant
upside [to mutable static state], something to trade off against the
host of evils mentioned above? Well, not really. It’s just a
mistake, hallowed by long tradition. Which is why Newspeak has
dispensed with it.If Gilad is right, then it is a mistake for Harmony as well.
And if he's wrong, then...
I prefer to let Newspeak be Newspeak, and not try to force JS to fit a
different mold. Fixing a mistake such as the arguments object
(with ...rest and the spread operator), sure. "Fixing" the language,
say by never adding anything like a module system where the top
lexical frame of the module has mutable bindings, is not obvious a
fix. Many would call it a bug.
I believe it is. But as long as Harmony's module system admits a
subset which enforces the absence of static state, I'm happy enough.
Cool.
Agree we should try to avoid two layers, but we already have "the full
language" and SES. I'm skeptical (and I've said so!) that we can build
one module system for SES and find it usable for the full language,
but let's keep discussing.
I'd rather see alternative designs explored and common vocabulary and
understanding built up, so we can avoid missing something in an
exclusively bottom-up or top-down design exercise. So, grumpiness
aside, I'm delighted to see active discussion and multiple proposals.
On Tue, Feb 2, 2010 at 9:00 PM, <ihab.awad at gmail.com> wrote:
Create a context that allows only transitively immutable modules.
And the static rules for making this work could be to only allow top-level function definitions in the module, and freeze the exports before returning them. So:
/* an ocap module must look like this */
export function pointCartesian(x, y) { ... };
export function pointPolar(r, t) { ... };
but:
/* these are statically disallowed in an ocap module */
var x = 3;
const y = 4; // to be conservative
export var z = 5;
export const t = 3; // to be conservative
Ihab
On Tue, Feb 2, 2010 at 4:47 PM, <ihab.awad at gmail.com> wrote:
On Tue, Feb 2, 2010 at 9:00 PM, <ihab.awad at gmail.com> wrote:
Create a context that allows only transitively immutable modules.
And the static rules for making this work could be to only allow top-level function definitions in the module, and freeze the exports before returning them. So:
/* an ocap module must look like this */ export function pointCartesian(x, y) { ... }; export function pointPolar(r, t) { ... };
but:
/* these are statically disallowed in an ocap module */ var x = 3; const y = 4; // to be conservative export var z = 5; export const t = 3; // to be conservative
Note that disallowing const bindings is required, not just being conservative, since const doesn't mean deeply immutable.
On Tue, Feb 2, 2010 at 9:50 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
Note that disallowing const bindings is required, not just being conservative, since const doesn't mean deeply immutable.
Right. The specific example I showed was to a primitive, so that was, strictly speaking, conservative. The general case is as you say.
Ihab
Would someone mind posting a summary of the current positions of the active participants of this discussion, perhaps contrasting the proposals?
Kris Kowal
On Tue, Feb 2, 2010 at 10:01 PM, Kris Kowal <kris.kowal at cixar.com> wrote:
Would someone mind posting a summary of the current positions of the active participants of this discussion, perhaps contrasting the proposals?
The current positions are mostly in agreement.
Sam and Dave's proposal is similar to the require() of CommonJS: within what they call a Context (= CommonJS sandbox), module instance are shared.
Sam's suggestion is that a Context can be constructed which accepts only transitively immutable modules. This would mean that the ambiently available modules are powerless, which means they can be used in an ocap language manner. This Context would be what SES programmers would use.
This seems to be the winning proposal right now, since it does not overload any special forms yet allows both require()-style and capability language style programming.
I think there are details of how modules are named and identified that I, for one, need to think about, but there is far more flexibility to discuss these. The basics seem to be solid.
Ihab
Presuming that in the proverbial glossary a "Context" is what ES presently calls an execution context that has some intrinsic "Primordials", and a "Sandbox" is a mapping from unique module identifiers to modules (albeit instances or makers depending on what proposal you're talking about), does this proposal suggest that there is exactly one "Context" for every "Sandbox" and that any "module block statement" evaluated in a context populates the corresponding sandbox with a mapping from the given module identifier to the first class exports object of that module?
Kris Kowal
On Fri, Jan 29, 2010 at 5:13 PM, David Herman <dherman at mozilla.com> wrote:
We had a good discussion about modules at this week's meeting, and Sam Tobin-Hochstadt and I have worked out a strawman for a simple module system. I've posted a first draft to the wiki:
There are lots of examples to peruse at:
strawman:simple_modules_examples
which might also be a decent starting point to get a feel for the design.
This has a good bit of overlap with -- and is in many ways inspired by -- Ihab and Chris's work, but it has at least one major departure: modules in this proposal are not inherently first class values, although we do provide ways of getting objects that reflect the contents of modules.
This is just a draft, and we'll be revising it as we go along. But I'm happy to take any feedback people have. Also, please let me know what's unclear. I'll try to both clarify on es-discuss and revise the page accordingly.
Hi Dave and Sam,
I have now read the strawman and example pages carefully. I have not yet read this thread carefully, though I have skimmed it. Excuse me if some of my comments are redundant with or obsoleted by earlier messages. Given the agreement just now on the list, I have read your docs with this agreement in mind and found no conflicts.
-
You list "Dynamic upgrade" in the goals and then mention it again in a TBD. I do not understand how this proposal could be stretched to accommodate dynamic upgrade. I also do not think this is a criticism. I only mention it since it is one of your goals.
-
I find your import syntax too complicated and too redundant with other concept already on the proposals page. Given < harmony:destructuring>, I would
define your ImportDeclaration as
ImportDeclaration ::= 'import' Pattern 'from' ModuleSpecifier ';'
First, the trivial issue. The ES5 grammar specifies semicolons as literal semicolons as above and leaves it to other language to specify semicolon insertion rules. Your grammar says "(';')?", which confuses the issue. I assume this was either just informal or a typo.
With the above grammar, we no longer need your ImportSpecifier or ImportDeclarator productions. And we can also avoid YET ANOTHER OVERLOADING OF COLON. (Please keep in mind that type-like declarations probably will overload colon, so please let's keep it to a dull roar. Perhaps we should use '=' instead of 'from', in order to make the import production be more obviously an additional binding form.
Your.... Becomes....
import "Math" as M import M from Math import "Math": sum, pi import {sum, pi} from Math import "Math": sum as sam import {sum: sam} from Math
I do not like open import for the reason Mike Samuel stated: adding an exported symbol to Math should not break existing clients of Math. If all those clients only do closed import as above, it won't. If they do open imports from Math and one other module, then the newly exported name may conflict with another open import, breaking existing clients. Granted, they break with an early error, but still. In any case, if we do want open import, I think a better syntax is
Your.... Becomes....
import "Math" import {*} from Math
or perhaps
import "Math" import * from Math
The use of * for open import will be instantly familiar to Java programmers (and instantly alarming to Java programmers like me who have learned to be alarmed by it).
Note that open issue is not a security issue per se, and has nothing to do with SES vs Harmony. It is purely a software engineering and maintenance issue.
I do not understand why the ModuleSpecifier may be a list of strings. A list of strings is less than you need to express where/how to find and verify that a source file may be the thing you intend to designate. Such lookup strategy issue should be internal to the module resolver. I like Ihab's packaging notion of a catalog, used to provide a module resolver with a mapping from simple module ids to potentially complex descriptions of where/how/verify. Given a catalog, a list of strings is more than you need for indexing into a cataloged description. A single module id should be fine.
With a ModuleSpecifier being a single module id, it could be an Id production rather than a StringLiteral (as also shown above).
For your Module production, what is the purpose of the Id after the "module" keyword? After all, the module resolver is already assumed somehow able to find a named module by other means, such as its location.
I thought your notion of Script vs Module was interesting and probably right. It had not occurred to me to have them coexist in this way.
-
Since the most often exported thing will be functions (and in SES the only thing), should we allow
ExportDeclaration ::= 'export' Id '(' Params_opt ')' '{' FunctionBody '}'
as a shorthand for defining, freezing, and exporting a function?
-
By "VariableStatement", do you intend to include "const" and "let" declarations? These cannot be grouped into the same production because
if (e1) var x = 1;
is fine but
if (e1) let x = 1;
must be disallowed since lets don't hoist.
-
What is "modes for this"? You never again use "mode" in this manner.
-
Glad to see a TBD for creating new module resolvers. I think this is an important open issue.
-
I am very confused by your discussions of cyclic imports. How do you propose that a module instance object's exported property return undefined until its corresponding exported const variable is bound? For exported var variables, this makes sense, since var variables are immediately readable as undefined. For functions, of course, there's no problem; and therefore for SES there's no problem. But const and let variables normally have a read barrier, which throws on early read. I would hope to see this reflected in the module system. For example, if a
export const x = 8;
were in effect
// at top of module
Object.defineProperty(magicPreregisteredExports, 'x', {
get: const() {return x;},
enumerable: true
});
// at original location
const x = 8;
then an early attempt to read this property from the module instance object -- including by pattern matching -- would throw rather than silently give the wrong value.
-
Is the "can" in
But their properties can be explicitly updated in ECMAScript code, they cannot receive new properties, etc.
a misspelling of "cannot"?
-
In your optimization opportunities, you suggest that an
module A { import "B"; // do stuff }
could start executing once the module resolver knows it can resolve B but before it knows whether B itself will have an early error. This implies that the semantics of importing a module with an early error is not that the importing module itself has an early error. What alternative semantics for B's early error do you suggest for A? (There is a parenthetical comment that might be about this at the beginning of "Dynamic module loading". But I don't know since I didn't understand it either.)
Example document
-
What does the fragment in
mean?
-
What is "offline ES"? Do you mean server-side?
-
I really like your "DM.make(Dom)" example. Good show.
On Tue, Feb 2, 2010 at 5:03 PM, Kris Kowal <kris.kowal at cixar.com> wrote:
Presuming that in the proverbial glossary a "Context" is what ES presently calls an execution context that has some intrinsic "Primordials", and a "Sandbox" is a mapping from unique module identifiers to modules (albeit instances or makers depending on what proposal you're talking about), does this proposal suggest that there is exactly one "Context" for every "Sandbox" and that any "module block statement" evaluated in a context populates the corresponding sandbox with a mapping from the given module identifier to the first class exports object of that module?
Having just reread the docs carefully, I did not take them to imply that.
Rather, there's a TBD issue for how to create new module resolvers and by implication, create new module loaders (i.e., sandboxes in the stateful non-SES usage) using that resolver. Thus, once this TBD is decided, I would expect one can have multiple resolvers and loaders/sandboxes per Context.
Not quite sure how to unpack the question. Let me try a quick sketch, at least for the simple modules system:
- a "module ID resolver" is a mapping from module ID's to module instances
- a "module context" is a set of module instances
- a "module context object" is a first-class value representing a module context
A module has at most one instance per module context, but may be instantiated separately in separate contexts. Module contexts might share some module instances; for example, the standard library might be shared. There might be a facility for explicitly constructing module contexts that share some module instances. Not sure.
Different module contexts may have different module ID resolvers, so for example it would be possible for host environments to provide a SecureESContext that didn't allow identifiers to resolve to the "filesystem" module or the "dom" module.
There would be an API for dynamically evaluating code in a given context; roughly a `loadModule' that takes a Context argument (or is a method of Context).
Does this help?
On Tue, Feb 2, 2010 at 5:27 PM, David Herman <dherman at mozilla.com> wrote:
On Feb 2, 2010, at 5:03 PM, Kris Kowal wrote:
Presuming that in the proverbial glossary a "Context" is what ES presently calls an execution context that has some intrinsic "Primordials", and a "Sandbox" is a mapping from unique module identifiers to modules (albeit instances or makers depending on what proposal you're talking about), does this proposal suggest that there is exactly one "Context" for every "Sandbox" and that any "module block statement" evaluated in a context populates the corresponding sandbox with a mapping from the given module identifier to the first class exports object of that module?
Not quite sure how to unpack the question. Let me try a quick sketch, at least for the simple modules system:
- a "module ID resolver" is a mapping from module ID's to module instances
- a "module context" is a set of module instances
Please call this something else. It is confusing for "context" to mean "execution context" and "module context" depending on the "context". I've called this a "system of modules" or "sandbox of modules" in the past.
Different module contexts may have different module ID resolvers, so for example it would be possible for host environments to provide a SecureESContext that didn't allow identifiers to resolve to the "filesystem" module or the "dom" module.
This verbiage implies black-listing. It would be good to be clear that the object formerly known as a "module context" should be explicitly populated with a white-list of module instances for SES.
There would be an API for dynamically evaluating code in a given context; roughly a `loadModule' that takes a Context argument (or is a method of Context).
Tell me more about this "loadModule" and how it works. I am concerned that modules would be "autonomous"; I firmly believe that a chunk of code should never select its own name, or more to the point, it should never be necessary to edit code to give a module, or a tree of modules, an alternate name. That kind of coupling has wasted many days of my time, integrating disparate projects with tight linkage.
Kris Kowal
P.S., for some liesure reading, here is the Narwhal CommonJS "module context" implementation: 280north/narwhal/blob/master/lib/sandbox.js, 280north/narwhal/blob/master/lib/loader.js
I thought I would also mention that contexts (as in execution context), sandbox,
security and other terms are also heavily used in html5. Defining a module framework
without at least being familiar with current and upcoming browser environments
may result in an implementation that is at odds with html5 entities which use
similar terminology. For example, a SecureESContext would probably prevent
cross-document messaging, channel messaging and other mechanisms that exist or
are being defined in html5. Strictly speaking cross-document messaging violates
TCB since existence of adjacent iframes and their source domains can be determined.
Yes I'm not sure current module strawmen define a general mechanism where
module-to-module communication can occur.
Perhaps to complement the discussion, there is a variety of literature which describes modules in different ways. Given one goal of harmony is to make programming in the large easier, I would think a module framework's first goal is to make it easier to organize code.
Some papers on modules with a few choice quotes.
Functions, Frames and Interactions. Claus Reinke. 'A module is a ... collection ... of elements. It provides a separate namespace with explicit control of import/export.'
Modular Object-Oriented Programming with Units and Mixins. Findler, Flatt 'A module delineates boundaries for separate development...'
Metalevel Building Blocks for Modular Systems. Jagannathan 'allow ... module elaboration with minimal program rewriting...'
The Influence of Software Module Systems on Modular Verification. Li, Fisler, Krishnamurthi. 'feature-oriented modules ... simplify key software engineering problems such as configurability, maintainability, and evolution.'
kam
On Tue, Feb 2, 2010 at 5:16 PM, Mark S. Miller <erights at google.com> wrote:
On Fri, Jan 29, 2010 at 5:13 PM, David Herman <dherman at mozilla.com> wrote:
We had a good discussion about modules at this week's meeting, and Sam Tobin-Hochstadt and I have worked out a strawman for a simple module system. I've posted a first draft to the wiki:
There are lots of examples to peruse at:
strawman:simple_modules_examples
which might also be a decent starting point to get a feel for the design.
This has a good bit of overlap with -- and is in many ways inspired by -- Ihab and Chris's work, but it has at least one major departure: modules in this proposal are not inherently first class values, although we do provide ways of getting objects that reflect the contents of modules.
This is just a draft, and we'll be revising it as we go along. But I'm happy to take any feedback people have. Also, please let me know what's unclear. I'll try to both clarify on es-discuss and revise the page accordingly.
Hi Dave and Sam,
I have now read the strawman and example pages carefully. I have not yet read this thread carefully, though I have skimmed it. Excuse me if some of my comments are redundant with or obsoleted by earlier messages. Given the agreement just now on the list, I have read your docs with this agreement in mind and found no conflicts.
You list "Dynamic upgrade" in the goals and then mention it again in a TBD. I do not understand how this proposal could be stretched to accommodate dynamic upgrade. I also do not think this is a criticism. I only mention it since it is one of your goals.
I find your import syntax too complicated and too redundant with other concept already on the proposals page. Given < harmony:destructuring>, I would define your ImportDeclaration as
ImportDeclaration ::= 'import' Pattern 'from' ModuleSpecifier ';'
First, the trivial issue. The ES5 grammar specifies semicolons as literal semicolons as above and leaves it to other language to specify semicolon insertion rules. Your grammar says "(';')?", which confuses the issue. I assume this was either just informal or a typo.
With the above grammar, we no longer need your ImportSpecifier or ImportDeclarator productions. And we can also avoid YET ANOTHER OVERLOADING OF COLON. (Please keep in mind that type-like declarations probably will overload colon, so please let's keep it to a dull roar. Perhaps we should use '=' instead of 'from', in order to make the import production be more obviously an additional binding form.
I withdraw the '=' suggestion. After an additional moment's thought, it was obviously stupid. For example
import M = Math;
does succeed at more strongly suggesting that M is a pattern. However, it does it at the price of suggesting that Math is an expression, which it isn't. The additional clarity on the left is much less than the additional confusion on the right. Let's stick with 'from'.
Is there a fundamental theoretical comp sci reason why module state is shared? Is the core idea of using lexical scoping in the simple_module proposal undermined by not having shared state.
If not, could possibly non-shared state be the default behaviour. And shared state modules - which share state within contexts - are somehow marked as shared at module definition. e.g. module ModShared { "use shared" // or some mechanism to signify shared state ...
On Feb 2, 2010, at 5:16 PM, Mark S. Miller wrote:
- I find your import syntax too complicated and too redundant with
other concept already on the proposals page. Given <harmony:destructuring, I would define your ImportDeclaration as
ImportDeclaration ::= 'import' Pattern 'from' ModuleSpecifier ';'
Hi Mark, I too had a few bikeshed/usability/redundancy thoughts
similar to yours on syntax. The color of the paint is the least of
these three issues, so this seems worth discussing (below).
First, the trivial issue. The ES5 grammar specifies semicolons as
literal semicolons as above and leaves it to other language to
specify semicolon insertion rules. Your grammar says "(';')?", which
confuses the issue. I assume this was either just informal or a typo.
Just a n00b thing ;-) -- I took the liberty of fixing this.
With the above grammar, we no longer need your ImportSpecifier or
ImportDeclarator productions. And we can also avoid YET ANOTHER
OVERLOADING OF COLON. (Please keep in mind that type-like
declarations probably will overload colon, so please let's keep it
to a dull roar. Perhaps we should use '=' instead of 'from', in
order to make the import production be more obviously an additional
binding form.Your.... Becomes....
import "Math" as M import M from Math import "Math": sum, pi import {sum, pi} from Math import "Math": sum as sam import {sum: sam} from Math
I too advocated import ... from M; in conversations with Dave. It's
prettier. The destructuring binding analogy only goes so far, though,
since the module id does not denote an object (these are second class
modules in this context) and indeed (Allen's suggestion) may be a
special form not similar to any object expression.
Also, the braces are a bit much, especially with * (below). The []
array pattern does not make sense, so it is plausible to drop the
requirement for outermost braces.
It's also plausible to keep them, as much for intuitive precedence of
"from" over "unbraced comma" as for consistency with destructuring.
What I'm getting at is this: comma is the lowest precedence operator,
and when used as a separator (e.g. in actual parameter lists) it
separates higher-precedence AssignmentExpressions. Yet
import x, y as yy, z from M;
while unambiguous and parseable according to a sound grammar still may
be too different in its use of comma, |as| with higher precedence, and
|from| with lower precedence than comma.
It's a fine point; we should try writing examples this way often and
see where the usability sweet spot is.
import "Math" import * from Math
Right.
I see Allen's argument against the quoted-string module id as
attractive nuisance factored into your "Becomes..." alternatives, and
that's great. More to strawman-specify here, but I believe everyone
agrees with Allen's point. (I hope we can avoid the hated :: separator
in the special form for module ids, however! Yet . also has issues --
more later.)
With a ModuleSpecifier being a single module id, it could be an Id
production rather than a StringLiteral (as also shown above).
I believe we'll need more than a flat Identiifier to denote modules
when internally linking. This gets at the next point, however:
For your Module production, what is the purpose of the Id after the
"module" keyword? After all, the module resolver is already assumed
somehow able to find a named module by other means, such as its
location.
Simple modules do not want to require external linking, catalogs, etc.
Part of the simplicity is starting small and growing, just by using
files or script tags depending on your embedding of the language.
Doing so means it should be possible to specify module ids when
defining as well as when importing.
Since the most often exported thing will be functions (and in SES
the only thing), should we allowExportDeclaration ::= 'export' Id '(' Params_opt ')' '{'
FunctionBody '}'as a shorthand for defining, freezing, and exporting a function?
I remember your examples in the wiki doing a similar thing with const.
It might be the right short-hand, including freezing. It's pretty
sweet, although using 'let' or 'var' to define a function without the
'function' keyword, e.g.
let f(a,b,c) { return a*b + c; }
looks like "a bridge too far" to me: is f hoisted to top of block, or
to top of program if no braced block encloses this definition, but
given the initial value |undefined|? 'function' gives a letrec binding
without linking the related functions and it's old as the hills.
I've found OCaml's allowing let to bind a function less helpfully
different than SML's separate fun binding form, when reading and
searching code. This is the practical side of "different meanings
should have different syntaxes". It matters more than epsilon for
programmer productivity in my experience.
We need to avoid too much redundancy among short- and long-hands. We
should avoid symmetry breaks where the shorter form has confusingly
different meaning. But the latter point does not argue against
implicit freezing in the case of |export f(...) ...|, IMHO.
By "VariableStatement", do you intend to include "const" and "let"
declarations? These cannot be grouped into the same production becauseif (e1) var x = 1;
is fine but
if (e1) let x = 1;
must be disallowed since lets don't hoist.
Good catch; I think the VariableStatement referenced was ES5's or
ES1-3's for that matter. Need to split cases for 'let'.
- I am very confused by your discussions of cyclic imports. How do
you propose that a module instance object's exported property return
undefined until its corresponding exported const variable is bound?
For exported var variables, this makes sense, since var variables
are immediately readable as undefined. For functions, of course,
there's no problem; and therefore for SES there's no problem. But
const and let variables normally have a read barrier, which throws
on early read.
Good point.
I would hope to see this reflected in the module system. For
example, if aexport const x = 8;
were in effect
// at top of module Object.defineProperty(magicPreregisteredExports, 'x', { get: const() {return x;}, enumerable: true }); // at original location const x = 8;
then an early attempt to read this property from the module instance
object -- including by pattern matching
Nit, not nagging but this is important: destructuring is not pattern
matching, critically because of the lack of alternative choice, cut,
etc. Indeed destructuring will happily bind |undefined| if you ask for
a missing property name or index.
True matching, for things like JSON and AST processing, could be
valuable as a future extension; separate topic, more later.
-- would throw rather than silently give the wrong value.
I'll leave this question for Sam&Dave but I agree that the module
instance object would have to implement a read barrier in order to
throw for early access to const and let, as you suggest.
Is the "can" in
But their properties can be explicitly updated in ECMAScript
code, they cannot receive new properties, etc.a misspelling of "cannot"?
Fixed.
In your optimization opportunities, you suggest that an
module A { import "B"; // do stuff }
could start executing once the module resolver knows it can resolve
B but before it knows whether B itself will have an early error.
This implies that the semantics of importing a module with an early
error is not that the importing module itself has an early error.
What alternative semantics for B's early error do you suggest for A?
(There is a parenthetical comment that might be about this at the
beginning of "Dynamic module loading". But I don't know since I
didn't understand it either.)
Seems early errors should be well-ordered and blamed on the directly-
erroneous module, so we can't proceed with evaluating A until B has
been evaluated. But this is likely, given that a resolved B will have
been parsed when loaded, in common implementations. The spec should be
normative here, I agree.
I was chatting to Maciej about all this and he asked whether eval(s)
where s contains import M is allowed. If M can't be prefetched this
would be a blocking import within eval, violating the execution model
in the face of nested event loops, mutation from other events, etc. Or
it would be a "modal" blocking import, like a modal dialog -- even
worse.
Probably import should not be allowed in eval code.
What does the fragment in
mean?
I read this as more plausibility-argument sketching, not (yet) a
detailed proposal. If we don't think it should be developed into a non-
sketchy proposal, it should be cut.
- What is "offline ES"? Do you mean server-side?
Or disconnected client, I took it to mean.
Thanks for the detailed feedback.
- You list "Dynamic upgrade" in the goals and then mention it again in a TBD. I do not understand how this proposal could be stretched to accommodate dynamic upgrade. I also do not think this is a criticism. I only mention it since it is one of your goals.
Some of the pieces are there, or shadows of them anyway, insofar as you could create new contexts and dynamically instantiate modules. And if contexts can share other module instantiations it's at least conceivable that you could make it work. But I haven't thought it through much.
This isn't my #1 top priority, especially since you can achieve some of it at the program level (e.g., by dynamically deciding to call a new function that you loaded off the wire instead of your old function). But to support long-running web apps it's worth considering. And Erlang is probably the top place to look for prior art.
I find your import syntax too complicated and too redundant with other concept already on the proposals page. Given harmony:destructuring, I would define your ImportDeclaration as
ImportDeclaration ::= 'import' Pattern 'from' ModuleSpecifier ';'
I'm not crazy about this, but I'm not vehemently against, either. It's maybe a bit soon for syntax design. The proposal doesn't rest too strongly on it, at any rate.
The most important aspects, in my mind, are that:
-
None of the forms destroy static scoping.
-
It's reasonably easy to import all -- this convenience matters, even though I agree it's not great style for robust programs.
-
It's possible to rename bindings to work around ambiguities.
Both your suggestion and my strawman syntax achieve all these goals, I believe.
First, the trivial issue. The ES5 grammar specifies semicolons as literal semicolons as above and leaves it to other language to specify semicolon insertion rules. Your grammar says "(';')?", which confuses the issue. I assume this was either just informal or a typo.
It was meant to be optional semicolons but I didn't think about it carefully at all. I don't much care whether they're optional. As far as I'm concerned, if automatic insertion causes any difficulty, then we should just require 'em.
With the above grammar, we no longer need your ImportSpecifier or ImportDeclarator productions. And we can also avoid YET ANOTHER OVERLOADING OF COLON. (Please keep in mind that type-like declarations probably will overload colon, so please let's keep it to a dull roar. Perhaps we should use '=' instead of 'from', in order to make the import production be more obviously an additional binding form.
You've retracted your = suggestion, but I'm also not attached to the colon. It was just the first token I found that didn't look insane to me.
I do not like open import for the reason Mike Samuel stated: adding an exported symbol to Math should not break existing clients of Math. If all those clients only do closed import as above, it won't. If they do open imports from Math and one other module, then the newly exported name may conflict with another open import, breaking existing clients. Granted, they break with an early error, but still. In any case, if we do want open import, I think a better syntax is
Your.... Becomes....
import "Math" import {*} from Math
or perhaps
import "Math" import * from Math
I believe open import is crucial for simple scripts. The nice thing about second-class modules is it makes open import sane, statically scoped, and fail-fast. That is, in the upgrade-hazard you mention, the code is rejected at compile time since it's a static error to import the same binding from two modules. But you're right that the upgrade-hazard is still there. I believe this is a small price to pay for the convenience of rapid scripting.
I do not understand why the ModuleSpecifier may be a list of strings. A list of strings is less than you need to express where/how to find and verify that a source file may be the thing you intend to designate. Such lookup strategy issue should be internal to the module resolver. I like Ihab's packaging notion of a catalog, used to provide a module resolver with a mapping from simple module ids to potentially complex descriptions of where/how/verify. Given a catalog, a list of strings is more than you need for indexing into a cataloged description. A single module id should be fine.
Allen also made a similar point about not entangling the notion of module identifiers and configuration management / resource location. I agree we should make it possible to separate this out.
At the same time, I think it should be as absolutely dirt-simple as possible to migrate from non-module to module code, as well as to write short scripts. One way to do this is to allow module specifiers to directly indicate how to find a module, and then you can refactor it later to go through the extra indirection of a configuration table of some sort.
For example, you wouldn't want to require every script that uses a couple little libraries to create a separate table e.g.:
<script type="harmony-module-set">
module JSON = ["std://JSON", "http://json.org/json.js"];
module DOM = ["std://DOM"];
</script>
<script type="harmony">
import JSON as JSON;
import * from DOM;
alert(JSON.stringify({"hi":"world"}));
</script>
(No syntax quibbles; it's just for example.)
I'm not saying that's what you're proposing, just that any solution that does provide the ability to separate module identifiers from their location needs to keep the top-level burden at a minimum.
One quick strawman:
ModuleSpecifier ::= IndirectModuleSpecifier
| DirectModuleSpecifier
DirectModuleSpecifier ::= StringLiteral
| '[' StringLiteral*(',') ']'
IndirectModuleSpecifier ::= Identifier
| Identifier '.' IndirectModuleSpecifier
The "direct" syntax would basically tell the host environment how to locate the resource; the "indrect" syntax would look up in some user-provided configuration table. (If it's not user-provided, you have a standard-outside-the-standard problem.)
For your Module production, what is the purpose of the Id after the "module" keyword? After all, the module resolver is already assumed somehow able to find a named module by other means, such as its location.
So that you can define modules inline instead of requiring them all to be in separate files, which would especially be burdensome on the web. This way you can write:
<script type="harmony">
module A { ... }
module B { ... }
import A as A; import B as B;
...
</script>
Since the most often exported thing will be functions (and in SES the only thing), should we allow
ExportDeclaration ::= 'export' Id '(' Params_opt ')' '{' FunctionBody '}'
as a shorthand for defining, freezing, and exporting a function?
Worth considering.
By "VariableStatement", do you intend to include "const" and "let" declarations? These cannot be grouped into the same production because
if (e1) var x = 1;
is fine but
if (e1) let x = 1;
must be disallowed since lets don't hoist.
Good question. I drafted the grammar pretty quickly. These kinds of points probably do need refinement.
- What is "modes for this"? You never again use "mode" in this manner.
Different language modes. For example, I could imagine a convenient <script> type that gives you some convenient defaults:
<script type="harmony-script">
// the stdlib is already in scope
var now = new Date();
...
</script>
As for this', I actually didn't remember that I wrote that. :) I'm not sure whether
this' should always be initially null. To my thinking `this' is still TBD.
- I am very confused by your discussions of cyclic imports. How do you propose that a module instance object's exported property return undefined until its corresponding exported const variable is bound? For exported var variables, this makes sense, since var variables are immediately readable as undefined. For functions, of course, there's no problem; and therefore for SES there's no problem. But const and let variables normally have a read barrier, which throws on early read. I would hope to see this reflected in the module system.
Oh sure, fair enough. That's probably the right way to do it. Although it should perhaps not be possible to export a `let' binding; those really are supposed to be purely local.
Is the "can" in
But their properties can be explicitly updated in ECMAScript code, they cannot receive new properties, etc.
a misspelling of "cannot"?
A very inconvenient misspelling! :)
In your optimization opportunities, you suggest that an
module A { import "B"; // do stuff }
could start executing once the module resolver knows it can resolve B but before it knows whether B itself will have an early error. This implies that the semantics of importing a module with an early error is not that the importing module itself has an early error. What alternative semantics for B's early error do you suggest for A? (There is a parenthetical comment that might be about this at the beginning of "Dynamic module loading". But I don't know since I didn't understand it either.)
I don't think it says that-- you should be able to compile them in parallel, and whoever gets the first early error can report it. But you're not allowed to start executing early, unless you pause at the first observable side effect, until everyone's been checked for errors.
[Side note: this stuff gets very subtle; thankfully it's mostly the job of the compiler writers. :) Obviously we are responsible for making this sufficiently optimizable, but the point just being none of it reflects semantic complexity, so users don't have to worry about it much. When they care about performance, as long as the best practices are fairly sane, like "don't have too many large cyclic dependencies" and "order your module definitions by dependency so the leaves get loaded first," then I don't think the complexity is too burdensome.]
What does the fragment in
mean?
The Yahoo YUI library provides a module called `dom'. The idea of the hash symbol is that browsers might want to standardize on some way of selecting an individual module from a single URL that provides multiple module definitions. Perhaps the hash symbol is too cute; I don't know. This isn't our mandate to standardize; it's meant as a plausible example.
- What is "offline ES"? Do you mean server-side?
Yep.
Phew! Thanks again for the feedback.
On Feb 3, 2010, at 12:20 PM, David Herman wrote:
Although it should perhaps not be possible to export a `let'
binding; those really are supposed to be purely local.
The issue arises because we do want 'const' bindings to be exportable,
and they are supposed to be block-scoped exactly like 'let', but of
course written only by initialization (a mandatory syntactic
initializer) and not readable in the "temporal dead zone" before the
initializer has been evaluated and the const's value set.
[including the list, which got inadvertently cut]
Yes, I understand that short import is not "with", it just "smells" like "with" and like "global". Your reasons are correct for language implementers and I am telling about language users.
To be clear: this is not for implementers, it's for users. The point is that you can always know statically what is in scope. When you say
function f() { ... }
var x = ...
with(obj) {
f(x);
}
you don't know whether the references to f and x are captured by `obj' properties or not. When you say
import * from Foo;
f(x);
you know statically that f and x came from Foo. It doesn't depend on the runtime behavior of the program.
This is exactly about promoting good style. And when I proposed some shortcut for "function" 2-3 years ago in this list, it was discarded for good-style and readability reasons.
IIRC, it was discarded because a) we couldn't agree on a color for the bikeshed, b) it was determined that it didn't add enough utility to the language that wasn't already there.
My point is that it's kind of a deal-breaker for every script on the web to have to write e.g.:
<script type="harmony">
import alert, document, window from DOM;
...
</script>
People have not had much problem writing `function' since the mesozoic era of JavaScript. But if we want to get them to use a new module system, we can't impose undue burdens simply for the sake of "promoting good style." Especially when the downsides of ignoring that good style are not all that grave.
About burden - I see it - new item "Never use default import" will be added to books and code style guides after "Never use global and with". And every user will need to remember one more JS quirk.
And if I were writing a style book, I would not say "never use default import." I would say, "if you are writing a module of a reasonable size, or with a reasonable number of imports, or if it's not particularly clear, then don't use default import. Otherwise, go to town. It's your language, do what you see fit."
It's not a JS quirk, it's actually an important feature.
On Feb 2, 2010, at 6:23 PM, Kris Kowal wrote:
Different module contexts may have different module ID resolvers,
so for example it would be possible for host environments to
provide a SecureESContext that didn't allow identifiers to resolve
to the "filesystem" module or the "dom" module.This verbiage implies black-listing. It would be good to be clear that the object formerly known as a "module context" should be explicitly populated with a white-list of module instances for SES.
Agreed, and good point.
Oprah moment: something about the way you wrote makes me want to plead
for goodwill assumptions in our informal exchanges. No one on the
committee is trying to open up capability leaks or introduce ambient
authority. I doubt anyone is unfamiliar with the problems of
blacklisting. It seemed clear to me that Dave was not specifying
rigorously, just giving two examples.
(Ok, group hug :-P.)
On Feb 3, 2010, at 11:10 AM, Kevin Curtis wrote:
Is there a fundamental theoretical comp sci reason why module state
is shared?
Sam knows more about such things than I do, but my answer is "who
cares?" :-P Computer scientists and programming-language theorists do
not all agree on whether to countenance mutation, and if so, how to
handle it. But it is part of JS, and it's not going away. The simple
modules proposal starts with goals including:
Be compatible with:
Security mechanisms and idioms
State isolation mechanisms
But being compatible with these things does not mean making them the
only or default way.
Is the core idea of using lexical scoping in the simple_module
proposal undermined by not having shared state.
Probably, since the rest of the language does not combine lexical
scope and no-sharing by default.
More important: the core idea of "simple" modules is undermined.
Lexical scope is great and all, but separate. You can use lexical
scope and functions today to create singleton and multiple-instance
state sharing patterns.
Switching to lexical scope in modules is important if you are trying
to avoid stepping on your own, or your friends', toes -- especially
for early error checking.
Switching to unshared state by default is neither necessary nor
helpful to the simple_modules goal
Total compatibility with non-module code
Many languages with second-class module systems and lexical scope do
not make unshared the default. And you can control state sharing with
orthogonal tools (more below), so this is not a deal-breaker.
If not, could possibly non-shared state be the default behaviour.
And shared state modules - which share state within contexts - are
somehow marked as shared at module definition. e.g. module ModShared { "use shared" // or some mechanism to signify shared state
We've reached tentative agreement on how to subset the language so
that stateless, zero-authority modules can be the only modules
available to the secure subset, with anything powerful carefully
introduced ("whitelisted", see my reply to Kris just now; of course
whitelisting really is an overstatement, or over-broad statement in
ocap/POLA terms; and no one proposes blacklisting in the subset
language).
Let's not go back on this tentative agreement by trying to push the
full language away from sharing of mutable lexically bound state. It's
not going to fly in the simple modules proposal, because it removes
simplicity.
Even if the only change were to require a module to self-declare itsel
as "shared", to have teeth, that would mean the default (unshared)
module cannot be imported N times, N > 1, and have mutable singleton
state that it counts on. And that requires changes to the importing
code, to keep track of or memoize the singleton. It does not merely
change the way the shared-state module is defined. This approach
really wants Ihab's emaker-style modules.
With simple modules as proposed, if you want emaker-style modules (a
fine pattern), then use a function! JS already has first-class
functions, and with ES5 Object.freeze, etc., there is no overriding
need in the full Harmony language to wrap up the notion of module with
constructor function so tightly just to make non-shared mutable state
the default. Simplicity is best served by keeping primitives
orthogonal and composable.
I don't really think that "module state" should be a formal concept in a "second class" module system where modules are just units of static code organization. Such modules have little if anything to do with computational state. It is the declarative and imperative code within the module that creates and binds state. Modules, via import and export declarations, can cause identifier bindings to be shared but that says nothing about the "ownership" of the actual computational values that are accessed via those bindings.
It may be conversationally convenient to use the term "module state" to talk about the computational values and binding created by the code within a module but it really shouldn't mean anything much different from saying the "the state of source code lines 243-619"
(caveat, I'm ignoring module initialization order issues above but even that are more about temporal validity of bindings rather than the actual bound state.)
Allen
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Kevin Curtis Sent: Wednesday, February 03, 2010 11:11 AM To: Sam Tobin-Hochstadt Cc: Mark S. Miller; es-discuss at mozilla.org; Brendan Eich Subject: Re: simple modules
Is there a fundamental theoretical comp sci reason why module state is shared? Is the core idea of using lexical scoping in the simple_module proposal undermined by not having shared state.
If not, could possibly non-shared state be the default behaviour. And shared state modules - which share state within contexts - are somehow marked as shared at module definition. e.g. module ModShared { "use shared" // or some mechanism to signify shared state ...
- a "module context" is a set of module instances
Please call this something else.
Okay.
It is confusing for "context" to mean "execution context" and "module context" depending on the "context". I've called this a "system of modules" or "sandbox of modules" in the past.
Well, a "module system" is a language construct that provides modules. I think "sandbox" sort of suggests more isolation than is necessarily provided. PLT Scheme uses the worst possible name for the concept (I won't even say what it is, it's so awful).
I'll think about alternatives and update the wiki.
Different module contexts may have different module ID resolvers, so for example it would be possible for host environments to provide a SecureESContext that didn't allow identifiers to resolve to the "filesystem" module or the "dom" module.
This verbiage implies black-listing. It would be good to be clear that the object formerly known as a "module context" should be explicitly populated with a white-list of module instances for SES.
Okay; all I was saying was you could create any kind of restricted context you want, with whatever policies you want, in a security-oriented setting. But when there's a proposal please do inspect the verbiage.
...it should never be necessary to edit code to give a module, or a tree of modules, an alternate name. That kind of coupling has wasted many days of my time, integrating disparate projects with tight linkage.
A mechanism for specifying modules through a level of indirection, like Allen was urging, should make these kinds of problems solvable.
If not, could possibly non-shared state be the default behaviour. And shared state modules - which share state within contexts - are somehow marked as shared at module definition. e.g. module ModShared { "use shared" // or some mechanism to signify shared state ...
IMO, this would be draconian, insufficient on its own for security, and ad hoc.
Sometimes you want global state. Sometimes you want a module that memoizes everything. When you decide that you want control over the memoization (separate memoization tables, multiple instances of your memoizing data structures, etc.), you put the state in functions and/or objects and now it's not global anymore. That works well in ES already, and there's no need to restrict it.
That said, dialects (e.g. security-oriented dialects) would of course be free to provide such restrictions.
On Wed, Feb 3, 2010 at 8:50 PM, Allen Wirfs-Brock < Allen.Wirfs-Brock at microsoft.com> wrote:
It may be conversationally convenient to use the term “module state” to talk about the computational values and binding created by the code within a module but it really shouldn’t mean anything much different from saying the “the state of source code lines 243-619”
Thanks - that statement really clarified things for me. The simple_module
proposal is really a distinct approach as compared to using objects as modules.
Well, a "module system" is a language construct that provides modules. I think "sandbox" sort of suggests more isolation than is necessarily provided. PLT Scheme uses the worst possible name for the concept (I won't even say what it is, it's so awful).
I'll think about alternatives and update the wiki.
How about "module group"?
On Thu, Feb 4, 2010 at 10:11 AM, David Herman <dherman at mozilla.com> wrote:
How about "module group"?
But it's not a group of modules; it's a group of their instances (or whatever you want to call them -- the extension of the types in the modules).
Ihab
Yep. I thought ModuleInstanceGroup was a little over the top. :)
Well, a "module system" is a language construct
I not sure I agree with that characterization. A Module is a "language construct" as it as specific syntactic element of the language. It is a specific thing that you have to learn about when you "learn" the language.
It isn't clear to me that a "module system" a thing of that nature. Certainly, a specification device is needed to define the semantics of Module/ImportDeclaration/ExportDeclaration but I'm not sure whether that specification device and related concepts would need to be taught.
It seems what we are really grappling with is the concept of a groups of Application syntactic elements that are processed as a single semantically self consistent unit. In ES3/ES5 terms we are talking about a set of Program syntactic elements. The current spec. doesn't need to name this concept because it only deals with a single instance of the concept. Essentially the concept is "ambient".
If we are going to expand the ES specification to cover scenarios that require interaction among multiple instantiation of the of the current specification then we are going to have to explicitly deal with all of the ambient concepts that are implicitly in the current specification.
Before we worry too much about the naming of new concepts perhaps we should try to identified the existing ambient concepts that need to be explicit and then add any new only that are needed to support new features.
Well, a "module system" is a language construct
I not sure I agree with that characterization. A Module is a "language construct" as it as specific syntactic element of the language. It is a specific thing that you have to learn about when you "learn" the language.
I was pretty imprecise, sorry. All I meant was that informally, people use the phrase "module system" to describe the whole of the design. As in, everything. Example: "We've been discussing lately the design of a module system on es-discuss." Confusing to overload familiar terminology, esp. when it's in the same space.
Before we worry too much about the naming of new concepts perhaps we should try to identified the existing ambient concepts that need to be explicit and then add any new only that are needed to support new features.
Sure, we're painting the specification bikeshed. We will need to agree on terms at least provisionally before long. But it can wait till there's content on the wiki.
On Wed, Feb 3, 2010 at 3:11 PM, David Herman <dherman at mozilla.com> wrote:
Well, a "module system" is a language construct that provides modules. I think "sandbox" sort of suggests more isolation than is necessarily provided. PLT Scheme uses the worst possible name for the concept (I won't even say what it is, it's so awful).
I'll think about alternatives and update the wiki.
How about "module group"?
Since, AFAICT, the concept being named here, in Java, corresponds to a ClassLoader, I suggest "loader". (E calls these "loaders" as well.)
I like it. I might prefer "module loader" for a bit more concreteness. But it has the benefit of concreteness and familiarity.
On Feb 3, 2010, at 4:07 PM, David Herman wrote:
I like it. I might prefer "module loader" for a bit more
concreteness. But it has the benefit of concreteness and familiarity.
+1 on module loader, loader for short. Thanks, Mark!
It's still not clear to me what we are trying to name?
According to the proposal, a Module is a syntactic element that is part of an Application and Application can consist of multiple Modules. A complete Application is presumably represented by an external container such as a source file so we will presumably "load" Applications, not Modules. (If "load" is even the correct concept, I don't see any reasons you couldn't build a Harmony implementation where you feed a bunch of such Application source containers to an Harmony compiler that generated a self-contained binary exe. Where does the "loading" take place in that scenario?)
Are we trying to name the specification mechanism that is used to describe the semantic association between ImportDeclarations and ExportDeclarations or are we trying to name a hypothetical extensible mechanism of a Harmony implementation that is used to identify and located the external containers of Applications, or are we naming a specific semantic abstraction that we intend to reify that permits some sort of dynamic intercessions in the binding of ModuleSpecifiers, or something else?
(sorry, if I'm being a pain about this but it is pretty clear that everybody is reading lots of implications into the names that are being thrown around)
Allen
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of David Herman Sent: Wednesday, February 03, 2010 4:07 PM To: Mark Miller Cc: es-discuss Steen Subject: Re: simple modules
I like it. I might prefer "module loader" for a bit more concreteness. But it has the benefit of concreteness and familiarity.
Dave
On Feb 3, 2010, at 4:03 PM, Mark Miller wrote:
On Wed, Feb 3, 2010 at 3:11 PM, David Herman <dherman at mozilla.com<mailto:dherman at mozilla.com>> wrote:
Well, a "module system" is a language construct that provides modules. I think "sandbox" sort of suggests more isolation than is necessarily provided. PLT Scheme uses the worst possible name for the concept (I won't even say what it is, it's so awful).
I'll think about alternatives and update the wiki.
How about "module group"?
Since, AFAICT, the concept being named here, in Java, corresponds to a ClassLoader, I suggest "loader". (E calls these "loaders" as well.)
Sorry for the confusion-- we're discussing a name for something that is not part of the current strawman. One of the things Sam and I were trying to do was separate the concerns of modularity and isolation. So there's a not-fully-worked-out strawman waiting to be written for isolation. That's what we're talking about a name for.
The rough idea of the impending strawman is that there would be the ability to create a new ModuleLoader with which one could load modules completely isolated from the current setting. Thus every instance of a module would be tied to a particular loader. Per loader, there would never be more than one instance of a given module, but in an application with multiple loaders, there might be multiple distinct instances of the same module.
Sorry to have confused things by discussing a non-existent (yet) proposal. :/
On Thu, Feb 4, 2010 at 12:06 PM, David Herman <dherman at mozilla.com> wrote:
The rough idea of the impending strawman is that there would be the ability to create a new ModuleLoader with which one could load modules completely isolated from the current setting.
Isn't that just the "pim" portion of the emaker style modules proposal?
Ihab
On Thu, Feb 4, 2010 at 11:50 AM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
... or are we naming a specific semantic abstraction that we intend to reify that permits some sort of dynamic intercessions in the binding of ModuleSpecifiers, ...
Yes, I think. Does everyone agree?
Ihab
OK, I think I get the concept. I certainly agree that there is a real world need for a unit of isolation for the processing of groups of Applications (in the simple module proposal sense or Programs in the ES3 sense) . Such a unit would presumably represent independent processing of the group of Applications including rejecting conflicting Module declaration, input/export binding, etc.
Here are some things that seem less clear:
The semantics of Applications and the combination of applications including name binding are (at ultimately will be) will fully specified. The actual identification and access mechanism of the external containers of the Applications are current outside the scope of the ES specification. In addition, I think there is fairly broad agreement that module identifiers shouldn't embed configuration or container identification information. Given this, is there actually any need for a standard mechanism for dynamic intercessions with the process of populating the unit with Applications? It seems that the only thing that could be done that wouldn't potentially change the fully specified semantics of these language constructs is the handling of the external containers that is something that, so far, has been placed out of scope.
There is more involved in these units than just Module binding. There is the "global" object and the primordial bindings to consider. There presumably would be some dynamic communications path or state sharing between units that needs to be established. Possibly other things. Do we really want to treat each of these aspects completely independently? Can we simplify things to a single "unit" concept.
Isn't most of what is involved in establishing such one of these isolation units exactly the same as what is involved in establishing the "global" unit?
The central concept here doesn't seem to be about "modules". Those are just an organizing concept for ECMAScript source code. It also doesn't seem be about an actor as might be given a action oriented name such as "loader" Instead it seems about something much more existential in nature. These units are things that may come into existence. When they do, they have certain fixed and variable characteristics.
So, here is how I might start to organize and name some of these concepts
Subprogram: One of the "Units of isolation". Application: The syntactic unit from the modules proposal. (alternatively, consider "Fragment" in place of "Application". As in source code fragment.) Program: A dynamic set of objects that are manipulated by a dynamic set of Subprograms. Every Programs initially has one Subprogrm.
A Subprogram integrates a set of Applications, along with, a independent set of primordial objects, and a global object. It includes all the mechanisms necessary to do Module import/export binding and the other normal ECMAScript bindings.
Programs initially have one Subprogram. The code of that Subprogram may cause other Subprogram to come into existence and establish any necessary object sharing or other communications channels with them.
From: David Herman [mailto:dherman at mozilla.com] Sent: Wednesday, February 03, 2010 5:06 PM To: Allen Wirfs-Brock Cc: Mark Miller; es-discuss Steen Subject: Re: simple modules
Sorry for the confusion-- we're discussing a name for something that is not part of the current strawman. One of the things Sam and I were trying to do was separate the concerns of modularity and isolation. So there's a not-fully-worked-out strawman waiting to be written for isolation. That's what we're talking about a name for.
The rough idea of the impending strawman is that there would be the ability to create a new ModuleLoader with which one could load modules completely isolated from the current setting. Thus every instance of a module would be tied to a particular loader. Per loader, there would never be more than one instance of a given module, but in an application with multiple loaders, there might be multiple distinct instances of the same module.
Sorry to have confused things by discussing a non-existent (yet) proposal. :/
Dave
On Feb 3, 2010, at 4:50 PM, Allen Wirfs-Brock wrote:
It's still not clear to me what we are trying to name?
According to the proposal, a Module is a syntactic element that is part of an Application and Application can consist of multiple Modules. A complete Application is presumably represented by an external container such as a source file so we will presumably "load" Applications, not Modules. (If "load" is even the correct concept, I don't see any reasons you couldn't build a Harmony implementation where you feed a bunch of such Application source containers to an Harmony compiler that generated a self-contained binary exe. Where does the "loading" take place in that scenario?)
Are we trying to name the specification mechanism that is used to describe the semantic association between ImportDeclarations and ExportDeclarations or are we trying to name a hypothetical extensible mechanism of a Harmony implementation that is used to identify and located the external containers of Applications, or are we naming a specific semantic abstraction that we intend to reify that permits some sort of dynamic intercessions in the binding of ModuleSpecifiers, or something else?
(sorry, if I'm being a pain about this but it is pretty clear that everybody is reading lots of implications into the names that are being thrown around)
Allen
From: es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org> [mailto:es-discuss-bounces at mozilla.org] On Behalf Of David Herman
Sent: Wednesday, February 03, 2010 4:07 PM To: Mark Miller Cc: es-discuss Steen Subject: Re: simple modules
I like it. I might prefer "module loader" for a bit more concreteness. But it has the benefit of concreteness and familiarity.
Dave
On Feb 3, 2010, at 4:03 PM, Mark Miller wrote:
On Wed, Feb 3, 2010 at 3:11 PM, David Herman <dherman at mozilla.com<mailto:dherman at mozilla.com>> wrote:
Well, a "module system" is a language construct that provides modules. I think "sandbox" sort of suggests more isolation than is necessarily provided. PLT Scheme uses the worst possible name for the concept (I won't even say what it is, it's so awful).
I'll think about alternatives and update the wiki.
How about "module group"?
Since, AFAICT, the concept being named here, in Java, corresponds to a ClassLoader, I suggest "loader". (E calls these "loaders" as well.)
On Wed, Feb 3, 2010 at 12:39 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Feb 2, 2010, at 6:23 PM, Kris Kowal wrote:
This verbiage implies black-listing. It would be good to be clear that the object formerly known as a "module context" should be explicitly populated with a white-list of module instances for SES.
Agreed, and good point.
Oprah moment: something about the way you wrote makes me want to plead for goodwill assumptions in our informal exchanges. No one on the committee is trying to open up capability leaks or introduce ambient authority. I doubt anyone is unfamiliar with the problems of blacklisting. It seemed clear to me that Dave was not specifying rigorously, just giving two examples.
(Ok, group hug :-P.)
My tone was definitely severe and the content lacked balancing concessions. My apologies to David.
Kris Kowal
On Wed, Feb 3, 2010 at 2:10 PM, Kevin Curtis <kevinc1846 at googlemail.com> wrote:
Is there a fundamental theoretical comp sci reason why module state is shared? Is the core idea of using lexical scoping in the simple_module proposal undermined by not having shared state. If not, could possibly non-shared state be the default behaviour. And shared state modules - which share state within contexts - are somehow marked as shared at module definition. e.g. module ModShared { "use shared" // or some mechanism to signify shared state
I want to give a little bit different answer to this, while still agreeing with Brendan and Allen.
Shared state is important because in a language with state, you want to be able to divide your program up into modules without changing its behavior. If you have a stateful piece of code, and you move it into its own module, that shouldn't change things any more than any other refactoring. If you need to repeatedly create fresh state, ES provides nice mechanisms for that as well. Similarly, if you have one module that imports A, and you divide it into two, both of which now import A, that refactoring shouldn't change how your program works.
So rather than a fundamental CS reason, the decision is based in the desire to support the way we think people will want to program. Obviously, we might be wrong about that, but that's how I see it.
On Wed, Feb 3, 2010 at 9:17 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
OK, I think I get the concept. I certainly agree that there is a real world need for a unit of isolation for the processing of groups of Applications (in the simple module proposal sense or Programs in the ES3 sense) . Such a unit would presumably represent independent processing of the group of Applications including rejecting conflicting Module declaration, input/export binding, etc.
Rather than adding a new term, it might be easier to just stick with Program from ES3/5. A Program would consist of some ES5-style code, and some modules.
Here are some things that seem less clear:
The semantics of Applications and the combination of applications including name binding are (at ultimately will be) will fully specified. The actual identification and access mechanism of the external containers of the Applications are current outside the scope of the ES specification. In addition, I think there is fairly broad agreement that module identifiers shouldn’t embed configuration or container identification information. Given this, is there actually any need for a standard mechanism for dynamic intercessions with the process of populating the unit with Applications? It seems that the only thing that could be done that wouldn’t potentially change the fully specified semantics of these language constructs is the handling of the external containers that is something that, so far, has been placed out of scope.
There is more involved in these units than just Module binding. There is the “global” object and the primordial bindings to consider. There presumably would be some dynamic communications path or state sharing between units that needs to be established. Possibly other things. Do we really want to treat each of these aspects completely independently? Can we simplify things to a single “unit” concept.
Isn’t most of what is involved in establishing such one of these isolation units exactly the same as what is involved in establishing the “global” unit?
The central concept here doesn’t seem to be about “modules”. Those are just an organizing concept for ECMAScript source code. It also doesn’t seem be about an actor as might be given a action oriented name such as “loader” Instead it seems about something much more existential in nature. These units are things that may come into existence. When they do, they have certain fixed and variable characteristics.
So, here is how I might start to organize and name some of these concepts
Subprogram: One of the “Units of isolation”.
Application: The syntactic unit from the modules proposal. (alternatively, consider “Fragment” in place of “Application”. As in source code fragment.)
Program: A dynamic set of objects that are manipulated by a dynamic set of Subprograms. Every Programs initially has one Subprogrm.
A Subprogram integrates a set of Applications, along with, a independent set of primordial objects, and a global object. It includes all the mechanisms necessary to do Module import/export binding and the other normal ECMAScript bindings.
Programs initially have one Subprogram. The code of that Subprogram may cause other Subprogram to come into existence and establish any necessary object sharing or other communications channels with them.
I think trying to name all of these conceptual entities just makes things more confusing. Here are the things that I think need discussion and names:
Module: the syntactic entity
Module name/identifier: how a Program refers to a particular module that it wants to import
Module Name Manager/Context/Module Loader: An entity that specifies how to find modules, given their names or identifiers. By creating these, programmers can manage what modules can be seen by what code. I think this (or the next item) is what Kris called a Context in his presentation at the face to face meeting.
Global state/??: Something that contains a global object, a set of primordials, a bunch of executable code, and a module name manager. This is I think what you're calling a Subprogram.
Note - global state is a terrible term, I'm just using it to have some name for the concept here.
Then there are a bunch of interesting questions to ask about the relationship between all of these entities.
- How is the mapping of what modules go with which names specified?
- Do we identify the global state with the name manager?
- Are primordials shared between different global states? Can they be? If they are, are they mutable (creating a channel of communication)?
- Can values be communicated between different global states?
I'm sure there are lots more questions here, which is why we really need to write something up and have something concrete to talk about.
On Thu, Feb 4, 2010 at 2:04 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
Module Name Manager/Context/Module Loader: An entity that specifies how to find modules, given their names or identifiers. By creating these, programmers can manage what modules can be seen by what code. I think this (or the next item) is what Kris called a Context in his presentation at the face to face meeting.
The module primordials proposal includes a Context constructor that creates what is presently an ES execution context and contains its intrinsic primordials. The concept of a Context in that proposal is completely orthogonal from the entity that manages the process of grabbing module code (Load/Loader) or linking and instantiating them (Require, which I've called a Sandbox in the past). It's my impression that the module statement in your proposal is a Registrar, or a module transport declaration [1] that enables modules to be transported in Programs. I do not understand the Linking and Instantiation semantics of the "Simple Modules" proposal, but it's possible that the concerns of registration, linkage and instantiation have been combined and additionally coupled to what I called the context, in which case it would be appropriate to call the entire entity a Context. However, I think that for the purpose of this discussion, it is desirable to separate the layers and concerns so that we can see the breadth of options.
Kris Kowal
Very nice...I really like the refactoring perspective. Essentially, there is a fairly simple refactoring transformation that can replace any sequence of top level ECMAScript code with a module. Basically, wrap it with a module declaration, add exports for any global declarations, add imports to any referenced "globals" (hmm...does the proposal have a way to do such an import), and add the necessary imports to the code that was left behind.
There probably are additional issues related to initialization order, but the basic concept is should be quite clear.
Allen
-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Sam Tobin-Hochstadt ...
On Thu, Feb 4, 2010 at 9:25 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu>wrote:
Shared state is important because in a language with state, you want to be able to divide your program up into modules without changing its behavior. If you have a stateful piece of code, and you move it into its own module, that shouldn't change things any more than any other refactoring. If you need to repeatedly create fresh state, ES provides nice mechanisms for that as well. Similarly, if you have one module that imports A, and you divide it into two, both of which now import A, that refactoring shouldn't change how your program works.
Great stuff - really clarifies things. I understand simple_module's as being like python modules or Delphi units or C #include (with a 1 to 1 relation between the .h, .c files). It should be possible to take seperate .js source files - with module {...}'s - and put them into one source file and the Program behaves the same. If a developer wants multiple instances of state - use objects or closures. This (for me) requires a bit of reorientation from the 'modules are (frozen) objects returned from function closures which contain the modules private state' pattern.
Interesting to see how modules work with native plaform functionality. It is said 'modules don't confer authority - objects do'. But aren't modules in someway authority bearing - even if indirectly via the objects they namespace. (No reply needed - i should wait for the Context/Isolation strawman - but this is what's on my mind :) ).
// Context/ModuleLoader A -- one.js import dom; // imports document document.x = 1;
-- two.js import dom; // imports document alert(document.x); // shows 1
// Context/ModuleLoader B - no dom authority -- three.js import dom; // no authority - error
N.B the import of module dom could be implicit/automatic - if the Context has 'authority' to the module. inter-Context communication - JSON only?
We had a good discussion about modules at this week's meeting, and Sam Tobin-Hochstadt and I have worked out a strawman for a simple module system. I've posted a first draft to the wiki:
There are lots of examples to peruse at:
which might also be a decent starting point to get a feel for the design.
This has a good bit of overlap with -- and is in many ways inspired by -- Ihab and Chris's work, but it has at least one major departure: modules in this proposal are not inherently first class values, although we do provide ways of getting objects that reflect the contents of modules.
This is just a draft, and we'll be revising it as we go along. But I'm happy to take any feedback people have. Also, please let me know what's unclear. I'll try to both clarify on es-discuss and revise the page accordingly.