[I've elided some points and comments: I was trying to summarize what seemed to me the core issues in this discussion; if my summary was unclear, it won't help to add more text; if my summary was clear, but the disagreements persist, adding more text won't help, either]

   Here I've come around to Isaac's opinion that 'import *' is a
   step too far. Previously, I said this is a convenient bad habit
   that might be left to linters. But that was based on experience
   with statically typed languages, where modules and their
   import/export interfaces could still be analyzed in separation.

   In ES, that is not the case: if 'System.set' and 'import *' are
   combined, humans and tools would have to *run* dependencies
   to discover the import interface. That makes it impossible to
   analyze/understand such modules in separation, statically.

This is not correct.  You can look at a single module in isolation,
and learn exactly the same things about its interface that you can in
Haskell, for example.

Haskell isn't a good role model wrt module systems - the main
design goal there was simplicity, so it doesn't use advanced module system ideas (at least not in the standard module system). Also, some good aspects have disappeared, and some aspects haven't quite scaled up with the increased use. One thing that disappeared (because it wasn't done well) was interface files, which allowed to develop modules wrt module interfaces rather than module implementations. So, yes, Haskell suffers from a combination of 'import * from M' with no easy way to pin down M's expected export interface.

Standard ML, and variants that support higher-order or even
first-class functors (parameterized modules), might be more
interesting in this context. Even when it can't be statically
(before running the module-level code) determined which module will provide the imports, one can pin down which interface that module will provide. So one can understand each module in isolation, with the import and export interfaces acting as boundaries.

But we can stay in ES6 for this discussion - consider

<script>
System.set('X',(Math.random() > 0.5) ? {x:"hi"} : {u:"oops"};
</script>

<script>
module M1 {
import * from X;
console.log(x.length);
}
module M2 {
import {x} from X;
console.log(x.length);
}
</script>

I cannot look at 'M1' and know whether or not 'x' is bound,
because the import interface is unspecified. So I'd have to
look at 'X' and, in this case, I'd have to run 'X' before I could
tell whether 'x' in 'M1' is going to be defined. In contrast, I can look at 'M2' and know, because the import interface is specified, that, if 'X' is accepted as dependency for 'M2', then 'x' will be available. There might still be a
problem at runtime, but it will be outside 'M2', and it will be
about matching an export interface to an import interface,
not about static scoping in 'M2'.

(Such dynamic module aspects are another reason why one
wants to keep exported, imported, and local names separate.)

   In brief, in the context of a language as dynamic as JS, the
convenience of 'import *' is not worth the damage it does to
modular program understanding. Instead, we should ensure
   that import interfaces are clearly and statically defined.

I disagree.  Clearly and statically defined interfaces are a great
thing for some software. Other programs, be they scripts written by middle school kids or dynamically-reflective towers of meta-programming, don't want or need them. What's the interface to `$`, in the face of jQuery plugins -- you can't tell, statically. But that doesn't mean plugins are a bad thing.

The question is not the export interface of '$', which can change
with every plugin or new release. The question is whether importers of '$' can isolate themselves from such changes by specifying an import interface. Any export interface that provides the import interface will do.

Of course, it is not just handy but a pragmatic necessity not having to write out 'standard' imports, but as with physical
'constants', things can go awry if 'standards' change. It would
be great if I could abstract over import interfaces, so that I
don't have to write them out on every import declaration.

One way to do this is via tools: for Haskell, I had a Vim plugin
that would allow me to write an unqualified variable, and
then have the plugin search the available module exports to
make suggestions about imports, adding the selected imports
and qualifiers. So I'd be free of worrying about writing import declarations, but my code would have the import interfaces documented.

Another way would use module loaders: the default System
loader already inserts implicit imports for some standard
modules, so it could insert those with explicit import lists.

And I could have a project-specific loader adding import
declarations for my 'standard' project imports.

And for school kids, one could have a course-specific loader
inserting course-specific import declarations. One could
even follow DrScheme in have level-specific 'standard'
imports. Or have a standard set of 'play around' imports.

I could be wrong, of course, but I think that there are other
(and better) solutions to the issues we are tempted to
address with 'import *'.

   import .. from 'loader!resource'

Dave and I have been talking about this, and fortunately it doesn't
require changing the core elements of the module system -- it just
means making the `System` loader somewhat more configurable at
runtime.  Then you'd be able to specify what the 'text' loader should
do, and it would automatically hand 'text!resource' off to that
loader, using the existing module loaders mechanism.  This wouldn't
reduce any of the benefits we get, as Dave listed earlier, but would
allow us to express the sorts of things you can do in AMD with loader
plugins.

Great!-) From what I could see of the discussion, this should
remove the main technical obstacles raised against upgrading
to ES6 modules. The translate hook should allow for things like Coffeescript or Streamline. The fetch hook ought to help with alternate sources (CDN with local fallback). The resolve hook ought to allow something like the RequireJS config (mapping abstract
module names to concrete resources in a central position).

Looking forward to ES6 modules,
Claus

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to