This makes it possible to load from the filesystem or network in direct style (instead of with callbacks) without blocking the main thread of execution.
Modules are used for scoping, and one of the top goals of Harmony is to preserve and promote static, lexical scoping. This avoids the many problems of dynamic scope: programming pitfalls (accidental or malicious scope injection), performance hazards, lack of modularity. Static scoping is also necessary for the Harmony feature of checking for unbound variables at compile time.
Modules provide the static definitions of a library, which are linked statically (see the “static modules” section).
If import declarations were resolved dynamically, then imported variables would be dynamically scoped. Harmony will not have any dynamic scoping (see the “static modules” section).
If exports could be removed, then imported variables would be dynamically scoped. Harmony will not have any dynamic scoping (see the “static modules” section).
This makes it possible for a library writer to have a minimal amount of local control over what clients can and can’t do. Of course, the library can always export objects whose contents can be mutated.
Secondarily, this also enables optimizations such as inlining and constant propagation for pre-defined system libraries, and dynamically for user-written const library exports once they’ve been initialized.
This would lead to a subtle difference between static module paths and dynamic module paths:
import m.n.o.dynamicallyAddedExport; // fails var x = m.n.o.dynamicallyAddedExport; // succeeds
openannotation allowing extensibility.
This would be more complex, and would suffer the same problem: static module paths would reject paths that might succeed at runtime. Modules can achieve extensibility by, for example, exporting a
This would make it very difficult to know what other code might have run before the module was sealed.
This would still be complex and suffer from the divergent behavior between static and dynamic module paths.
An alternative would be to have all top-level bindings be exported and require declaring hidden top-level bindings private to avoid exporting them. This is inconvenient and leads to accidental exports when creating local/temporary definitions in a module. CommonJS experience shows that it works well to have explicit exports.
This makes it convenient to modularize code cheaply, without having to create separate files. It is also consistent with the semantics that file top-level is implicitly a module but can contain sub-modules. Finally, it makes refactoring work well: code separated into modules can itself be separated into a new parent module.
This makes it so that files do not name themselves. Clients get to name the module, which ensures lack of module name conflicts, without the library having to contend with the entire world’s library names. It also makes versioning easy: multiple versions of a library can be separately loaded, without the library writer having to provide them with explicitly distinct names.
This is the only coherent semantics given the present of module instance caching. If a module can be loaded in multiple separate scopes within the same loader, it can’t possibly have all its clients’ scopes available to it. Moreover, this would create all sorts of accidental variable-capturing hazards.
import *form is included.
This is simply as a minor convenience, since ES5 and Harmony syntax allow the use of keywords in a property reference expression. Exported keywords cannot, of course, be imported as variable bindings using
=to bind module names.