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 <http://wiki.ecmascript.org/doku.php?id=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 allow
ExportDeclaration ::= '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 because
if (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 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
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
import 'http://developer.yahoo.com/modules/yui3.js#dom'
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.
/be
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss