1 of 32

Modules

Ihab Awad, Google

Kris Kowal, FastSoft

January, 2010

2 of 32

HATE is all you need

3 of 32

Hermetic eval *

  • empty scope chain
  • no global object
  • return last expression
  • well-defined approach to primordials

* - Hermetically Air-Tight Evaluator

4 of 32

Q&A

5 of 32

What we can do

Straw proposal for a possible system

Lexical scoping bounds connectivity of "foreign" code

6 of 32

Module Loader

  • resolves module identifiers,
  • fetches module text,
  • evaluates text, and
  • interns and returns a module function

7 of 32

Identifying a module

Module identifier - a name or location of a piece of code

Any arbitrary expression

Absolute or relative to "self" (or some other base)

"file:///usr/lib/js/util/linkedList.js"

"http://example.com/myModule.js"

{ name: "com.example.util.LinkedList",

  version: { min: "3.70", max: "4.00" } }

"../util/sortAlgorithms.js"

".util.sortAlgorithms"

8 of 32

Fetching a module

loader.fetch(id, opt_baseId)

Accepts a module identifier and an optional base.

Returns the module's text.

9 of 32

Evaluating a module

loader.evaluate(text)

Wrap in well-known envelope:

function evaluate(text) {

    return hermeticEval(

        "(function (require, exports) {" +

         text← ES Program production

        "})"

    );

};

Result: A (closed) module function

10 of 32

Inside a module function

Accepts:

  • a mechanism for importing
  • a mechanism for exporting
  • an environment

(function (require, exports) {

    require(id) ← importing

    exports.name = ... ← exporting

    ... = require.env.name ← environment

 })

11 of 32

Module instance

The result of invoking a module function:

require.env = {

  document: aDocument, window: aDocument

};

let exports = {};

moduleFunction(require, exports);

exports.freeze();

exports ← is the module instance

12 of 32

Module instance

13 of 32

A brief moment's reflection...

This is all we need       ← ya rly this time

      This is all we need

            We and some DI geeks

                  And a few ocap geeks

But → this is not how most people program

include "foo.js" ← expect a singleton instance

Principled way to do both styles?

14 of 32

Module Sandbox

  • an environment
  • a loader
  • interned module exports (~ instances)

15 of 32

Module sandbox

16 of 32

Module environment

{window, document, print, file}

Shared by all modules in a sandbox

The only conduit to side-effect the world (e.g., I/O)

New sandbox →

    New world of module instances

    ... to which creator is boundedly vulnerable

17 of 32

Module exports memo

Sandbox → 1 module instance per module function

Module functions and loaders may be safely shared

18 of 32

Syntax

19 of 32

Roadmap

  • current, global script
  • global scripts that can also be used as modules
  • modules that can be used with current ES and future ES
  • modules that have syntactic sugar

20 of 32

Syntactic variants

  • sugared and desugared
  • seasoned with salt
  • legacy global
  • exporting
  • importing
  • environment

21 of 32

Sugared & Desugared - Exporting

Sugar

export X =

  function (n) {

    return n + 1;

  };

export Y =

  function (n) {

    return X(n) + 1;

  };

Desugared

const X = exports.X =

  function (n) {

    return n + 1;

  };

const Y = exports.Y =

  function (n) {

    return X(n) + 1;

  };

22 of 32

Sugared & Desugared - Importing

Sugared

import "X" as Y;

import "X" as Y,

  "W" as Z;

from "X" import A;

from "X" import A, B;

from "X" import

  A as F;

Desugared

const Y =

  require("X");

const W =

  require("Z");

const {A} =

  require("X");

const {A, B} =

  require("X");

const {A: F} =

  require("X");

23 of 32

Sugared & Desugared - Environment

const {window, document} = require.env;

No special syntactic sugar.

24 of 32

Transitional ("Salt") - Export

Resembles the desugared form (in ES3 syntax) with one convenience:

exports.X = function (n) { return n + 1; }

exports.Y = function (n) { return X(n) + 1; }

Using a with block under the covers, we allow exported variables like X to appear in scope automatically.

25 of 32

Transitional ("Salt") - Import

Just like the desugared case, but without Harmony syntax.

26 of 32

Transitional ("Salt") - Environment

Just like the desugared case, but without Harmony syntax.

var window = require.env.window;

var document = require.env.document;

27 of 32

Legacy Global - Export

const X = this.X =

  function (n) {

    return n + 1;

  };

const Y = this.Y =

  function (n) {

    return X(n) + 1;

  };

To support this syntax, the sandbox must call the module function with the module exports bound to "this".

28 of 32

Legacy Global - Import

Say what?

29 of 32

Legacy Global - Environment

var window = 

  typeof require == "undefined" ?

  window :

  require.env.window;

In legacy global scripts, environment is promiscuous

30 of 32

HATE is all you need

31 of 32

Hermetic eval *

  • empty scope chain
  • no global object
  • return last expression
  • well-defined approach to primordials

* - Hermetically Air-Tight Evaluator

32 of 32

Q&A