[module] dynaimic namespace/scope

# Andrew Fedoniouk (12 years ago)

Consider this simple construct:

module "test" {
    export function setVar() {
        gvar = "GVAR";               // note 'gvar' is not defined in the module
    }
    export function getVar() {
        return gvar;
    }
}

module Test from "test";

Having this in place I am calling

Test.setVar();

Question: where is that gvar created? Possible case:

  1. {{globalns}}.gvar == "GVAR" // so in global namespace or
  2. {{globalns}}.Test.gvar == "GVAR" // in module namespace

In other words does the module establish its own dynamic namespace/scope?

If that behavior is spec'ed already I'll appreciate for the link.

# Rick Waldron (12 years ago)

It gets created nowhere, because the body of a module is implicitly strict, so the above code produces a Reference Error.

# Sam Tobin-Hochstadt (12 years ago)

On Wed, Jul 10, 2013 at 7:24 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

It gets created nowhere,

This is right.

because the body of a module is implicitly strict,

And this is right.

so the above code produces a Reference Error.

But this is incorrect, because modules check that their bodies don't have free variables, so the above code has a static error.

# Andrew Fedoniouk (12 years ago)

On Wed, Jul 10, 2013 at 4:24 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

It gets created nowhere, because the body of a module is implicitly strict, so the above code produces a Reference Error.

Thanks Rick. And I suspect there is no way to reclaim that strictness, right?

Asking because it could be useful in cases like this :

module "Safe" {
   "not use strict";
    export function eval(str) {
         return std.eval(str);
    }
}

And so if I will call:

Safe.eval("gvar=666") ;

it will not pollute global namespace.

# Andrew Fedoniouk (12 years ago)

On Wed, Jul 10, 2013 at 4:40 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

But this is incorrect, because modules check that their bodies don't have free variables, so the above code has a static error.

Sam, I don't think that generation of static check/error is possible in this case. {{globalns}}.gvar can be created after the module gets loaded/compiled but before Test.setVar() invocation.

# Allen Wirfs-Brock (12 years ago)

On Jul 10, 2013, at 4:40 PM, Sam Tobin-Hochstadt wrote:

But this is incorrect, because modules check that their bodies don't have free variables, so the above code has a static error.

Ah, do you mean this is a link time check?

It isn't a regular early error situations because gvar could be a dynamically added property on the global object.

Not sure what your current semantics are, but I thought it was established a long time ago that modules needed free references in order to deal with global object properties.

# Rick Waldron (12 years ago)

Thanks for the correction, I based my response on Appendix C of ES5.1, which of course doesn't know anything about module bodies ;)

# Rick Waldron (12 years ago)

On Wed, Jul 10, 2013 at 7:49 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:

Thanks Rick. And I suspect there is no way to reclaim that strictness, right?

One of the goals of ES6 (and all future versions) is to promote strict mode to become the "only" or at least "preferred" mode. Wherever reasonably possible new syntactic forms, such as class and module, will be designed as strict by default, with no "opt out".

# Sam Tobin-Hochstadt (12 years ago)

On Wed, Jul 10, 2013 at 8:02 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

But this is incorrect, because modules check that their bodies don't have free variables, so the above code has a static error.

Ah, do you mean this is a link time check?

It isn't a regular early error situations because gvar could be a dynamically added property on the global object.

Not sure what your current semantics are, but I thought it was established a long time ago that modules needed free references in order to deal with global object properties.

No, that's not what I mean. The semantics we've discussed multiple times are that modules are compiled with respect to the global object at compilation time, and free variables at that point (ie, compilation time) are a static error. This is implicit in the discussion here: harmony:module_loaders (see the "global objects" section) and explicit in some of the notes, I'm sure.

# Mark S. Miller (12 years ago)

On Wed, Jul 10, 2013 at 6:06 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu>wrote:

No, that's not what I mean. The semantics we've discussed multiple times are that modules are compiled with respect to the global object at compilation time, and free variables at that point (ie, compilation time) are a static error. This is implicit in the discussion here: harmony:module_loaders (see the "global objects" section) and explicit in some of the notes, I'm sure.

Yes, that is what I remember. A case I no longer remember though: What if a property is deleted from the global object after the module is compiled?

# Sam Tobin-Hochstadt (12 years ago)

On Wed, Jul 10, 2013 at 9:14 PM, Mark S. Miller <erights at google.com> wrote:

On Wed, Jul 10, 2013 at 6:06 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

No, that's not what I mean. The semantics we've discussed multiple times are that modules are compiled with respect to the global object at compilation time, and free variables at that point (ie, compilation time) are a static error. This is implicit in the discussion here: harmony:module_loaders (see the "global objects" section) and explicit in some of the notes, I'm sure.

Yes, that is what I remember. A case I no longer remember though: What if a property is deleted from the global object after the module is compiled?

This is still a dynamic error (a reference error, like Rick said), which I'm not really happy about. The only alternative I've thought of is changing such properties to non-configurable at compilation time of the module that references them. But that's a weirdly non-local effect, and is probably worse than the disease.

# Allen Wirfs-Brock (12 years ago)

On Jul 10, 2013, at 6:06 PM, Sam Tobin-Hochstadt wrote:

No, that's not what I mean. The semantics we've discussed multiple times are that modules are compiled with respect to the global object at compilation time, and free variables at that point (ie, compilation time) are a static error. This is implicit in the discussion here: harmony:module_loaders (see the "global objects" section) and explicit in some of the notes, I'm sure.

Hmm..has this been thought about in light of the decision to structure the global scope into to environment records (a object ER for the global object (and var/function declarations) and a declarative ER for let/consts, etc.)

Also, aren't free references (to globals) quite similar to imports that can't be satisfied at link time. I can imagine that such free variables could be treated as implicit imports from the global scope and resolved at the same time and in a similar manner to explicit imports. There is something that seems more satisfying about all "linking" errors occurring at the same time rather than some occurring as early errors and others occurring as linking errors.

Finally, are there races between an async script block that define modules and subsequent html elements with id attributes. If such a module uses a free global reference to access the DOM node with the matching id won't the occurrence of the compile time error depend upon whether or not the the async script loader gets around to compiler the the script before the html parser gets to the element with the id?

# Sam Tobin-Hochstadt (12 years ago)

On Wed, Jul 10, 2013 at 10:12 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Hmm..has this been thought about in light of the decision to structure the global scope into to environment records (a object ER for the global object (and var/function declarations) and a declarative ER for let/consts, etc.)

Yes, we've thought about this. I don't think it changes much, other than to make the case Mark worried about less prevalent.

Also, aren't free references (to globals) quite similar to imports that can't be satisfied at link time. I can imagine that such free variables could be treated as implicit imports from the global scope and resolved at the same time and in a similar manner to explicit imports. There is something that seems more satisfying about all "linking" errors occurring at the same time rather than some occurring as early errors and others occurring as linking errors.

I believe that this may end up being a distinction without a difference. When a module is compiled, the names it imports from other modules must be present, or there's a static error. There's no case where a module is compiled-but-not-linked, and thus I don't think the difference would be observable.

Finally, are there races between an async script block that define modules and subsequent html elements with id attributes. If such a module uses a free global reference to access the DOM node with the matching id won't the occurrence of the compile time error depend upon whether or not the the async script loader gets around to compiler the the script before the html parser gets to the element with the id?

There are certainly races here, just the way that:

<script async>
x
</script>

races with such dynamic DOM insertions.

# Allen Wirfs-Brock (12 years ago)

On Jul 10, 2013, at 7:33 PM, Sam Tobin-Hochstadt wrote:

There are certainly races here, just the way that:

<script async>
x
</script>

races with such dynamic DOM insertions.

yes, but sometime like:

<script async>
   function getNode1() {
      return node1   /
   }
   function getNode2() {
     return node2
   }
</script>
<div id='node1' />
<div id='node2' />

never produces an error (note no calls, but in practice I assume it would be structured such that any calls to either of the functions would only occurs after the async script load was known to have completed).

But if you try to modularize the code like:

<script async>
module "mod" {
   export function getNode1() {
      return node1
   }
   export function getNode2() {
     return node2
   }
}
</script>
<div id='node1' />
<div id='node2' />

then it may randomly work or fail with an error depending upon who wins the race. What seems like it should be a safe refactoring instead produces non-deterministic intermittent errors.

# Brian Di Palma (12 years ago)

This is still a dynamic error (a reference error, like Rick said), which I'm not really happy about. The only alternative I've thought of is changing such properties to non-configurable at compilation time of the module that references them. But that's a weirdly non-local effect, and is probably worse than the disease.

There is the alternative that you error on all non explicitly imported variables. If this system were being designed in a green field environment I'd imagine that's how it would be built.

I realize that means people would have to

import {console} from "std/console";

for logging, but that's hardly such a crippling issue once proper IDE support exists for ES6 modules.

I'd imagine auto complete will insert that at the top of your .js file if you are using it inside a module.

Why allow global scope to leak into a new module?

# Brian Di Palma (12 years ago)

But if you try to modularize the code like:

<script async>
module "mod" {
   export function getNode1() {
      return node1
   }
   export function getNode2() {
     return node2
   }
}
</script>
<div id='node1' />
<div id='node2' />

then it may randomly work or fail with an error depending upon who wins the race. What seems like it should be a safe refactoring instead produces non-deterministic intermittent errors.

The code would be

export function getNode2() {
  return document.getElementById("node2");
}

though which should be fine.

# Kevin Smith (12 years ago)

Why allow global scope to leak into a new module?

That would require a tedious preamble for pretty much any bit of code you want to write.

How about performing free variable checking as late as possible (i.e. just before the module body executes), giving module dependencies a chance to polyfill the global object if they so choose?

# Brian Di Palma (12 years ago)

That would require a tedious preamble for pretty much any bit of code you want to write.

You can add an exemption for any code/classes/functions in "@std" modules.

Beyond those cases I see no issue.

# Sam Tobin-Hochstadt (12 years ago)

On Thu, Jul 11, 2013 at 8:24 AM, Kevin Smith <zenparsing at gmail.com> wrote:

Why allow global scope to leak into a new module?

That would require a tedious preamble for pretty much any bit of code you want to write.

We agree, that's why we haven't tried to do this.

How about performing free variable checking as late as possible (i.e. just before the module body executes), giving module dependencies a chance to polyfill the global object if they so choose?

This is the other major option -- it delays errors, potentially to much later, which can be painful when it happens too late to catch. That's why we haven't gone with it currently.

# Claus Reinke (12 years ago)

Why allow global scope to leak into a new module?

That would require a tedious preamble for pretty much any bit of code you want to write.

We agree, that's why we haven't tried to do this.

You could have a standard preamble, implicitly included, with an option to override. That way, everything comes from a module, you don't have to import from the standard modules explicitly, and you could still override the standard imports if necessary.

Claus

# Brendan Eich (12 years ago)

Brian Di Palma wrote:

That would require a tedious preamble for pretty much any bit of code you

want to write.

You can add an exemption for any code/classes/functions in "@std" modules.

Beyond those cases I see no issue.

Object detection is a (if not the) successful anti-versioning pattern in JS on the web. It typically depends on global properties that may or may not be present, accessed in the detection condition via window.foo or typeof foo but then bound in the not-detected consequent, and used later, without any object-property base-expression (dot) qualification.

Object detection ought to be usable in a module, and IIRC this is another consideration informing the current design.

# Brian Di Palma (12 years ago)

Excellent point. I had forgotten all about polyfills.

You could work around that issue by allowing augmentation of the "@std" module in some manner...in essence that's what a polyfill does. Your polyfill code would be loaded up first, it would augment the standard module and then your application code would execute with no issues.

This would mean the standard module is not consistent with other modules so it's not a proposal I'm putting too much weight on.