Skip to content

Latest commit

 

History

History
932 lines (473 loc) · 41.9 KB

jul-27.md

File metadata and controls

932 lines (473 loc) · 41.9 KB

July 27, 2016 Meeting Notes


Brian Terlson (BT), Michael Ficarra (MF), Jordan Harband (JHD), Waldemar Horwat (WH), Tim Disney (TD), Michael Saboff (MLS), Chip Morningstar (CM), Daniel Ehrenberg (DE), Leo Balter (LEO), Yehuda Katz (YK), Jafar Husain (JH), István Sebestyén (IS), John Neumann (JN), Domenic Denicola (DD), Rick Waldron (RW), Stefan Penner (SP), Jonathan Sampson (JSN), Caridy Patiño (CP), Sam Tobin-Hochstadt (STH), John Buchanan (JB), Kevin Gibbons (KG), Lars Hansen (LHN), Peter Jensen (PJ), Tom Care (TC), Dave Herman (DH), Bradley Farias (BFS), Kris Gray (KGY), Adam Klein (AK), Dean Tribble (DT), Eric Faust (EFT), Jeff Morrison (JM), Sebastian Markbåge (SM), Saam Barati (SBI), Kris Gray (KGY), John-David Dalton (JDD), Ben Newman (BN), Morgan Phillips (MPS), Shu-yu Guo (SYG), Paul Leathers (PL), Ben Smith (BS), Zibi Braniecki (ZB)


9.i.g Date.parse fallback semantics

Morgan Phillips

https://github.com/mrrrgn/proposal-date-time-string-format

MPS: (see link) Date.parse accepts strings that are implementation specific. This is causing incompatibilities across runtimes with new and repeat bugs filed regularly.

Suggesting: specify the intersection of supported string formats, specify this as the fallback for Date.parse.

https://github.com/mrrrgn/proposal-date-time-string-format/blob/master/data/results.csv

DE: How much of what is currently allowed to parse is banned under this proposal?

AWB: In general, implementations are allowed to add extensions to do something else rather than throwing whenever they want.

DE: If any impl. doesn't apply extensions to spec: what sort of strings that currently parse, would not parse (by only implementing this format)?

WH: (what are the data in results.csv?)

MPS: Those are data generated by a fuzzer and the columns on the right are "yes" or "no" whether the string parsed

WH: The table lists which dates are accepted, but what dates do they generate? For a concrete example, line 30 is "may,may,-000000". It's accepted, but what date does that produce?

MPS: This will parse as May 1, 2000. Almost all of this is crazy, hacks on hacks

(shock and awe)

SP: Web compatibility concerns?

MPS: Working with V8 dev to add telemetry probes into V8 and SpiderMonkey. The web is fairly broken already—implementing an intersection as a fallback should not break the web.

SP: Is the goal to find the intersection, or a refinement on the intersection?

MPS: Refinement.

WH: Are there places where different impl. accept a string but generate different dates?

MPS: Possible, but haven't explored. So far have only looked for producing a NaN

SP: Should that be included?

MPS/AWB: Yes

AWB: Can we replace "yes" and "no" in the table with the actual output?

MPS: Yes

WH: Does the table include all "yes" rows (i.e. rows where the existing implementations all accept the string)?

MPS: removed, there are millions.

EFT: Those are covered by the proposed grammar

MPS: In the table there's a column for whether the proposed grammar accepts the string. It's the "parser" column.

WH: Row 4839 is accepted by three implementations but not the proposed grammar.

MF: There are 60 examples like that in the table.

MPS: The grammar needs to accept more. The current grammar isn't what I want to propose in the end.

AWB: I want to reference back to ES5 and when we added ISO dates and what our thinking at that time was. What we did in ES5, we introduced the ISO date format and we did a quick look at this sort of stuff, and decided it was such an incredible mess that maybe there's no way to fix it. Part of the thinking there was, do we fix up this thing that's here, and identify which dates we take and which we don't take, or do we leave and assume that some day in the future we'll introduce a new API that is a date parser, and that we've designed from first principles of what dates we want to parse, and we don't simply include funny weird cases that exist in implementation-defined stuff simply because it's there. Because it's a new API, we wouldn't have compatibility requirements. That's still possible--we could try to find this intersection, or we could leave it alone and find the date parser implementation that we want.

YK: [Agree, but Date API is generally the sort of thing that accepts junk and is expected to do the right thing]

DE: Unfortunately, browsers don't have the luxury of doing what AWB suggests, and the demands of web compatibility mean that we have to support what other browsers support.

MPS: noting reports that a company identified some large number of bugs being date-based.

DE: We have found that at least 4 out of 10k page loads use the "legacy date parser" in Chrome

Conclusion/Resolution

  • Stage 1 acceptance

9.iii.a System.global

Jordan Harband

https://github.com/tc39/proposal-global

JHD: We reached Stage 2 previously, and the remaining open question is which name should be used. window would break lots of code, since everyone assumes 'typeof window' indicates whether you're running in a main page. The remaining options are self, global, and System.global

DD: there's also frames! (jokingly)

JHD: This is about breaking libraries like lodash, as well as breaking code which runs in Node. self has for a long time indicated that your code is used on the web, but it doesn't seem like a really big hazard; not much code for that. Also, self is used to store the receiver, though you can use arrow functions for most of those use cases these days. var self = this is a frequent idiom. Ultimately, if we pick self, Node is broken.

DD: We (TC39) make breaking changes to Node all the time. I think "breaking Node" is overstating it

JHD: This is a very common pattern, though it sometimes dispatches on window or module.exports.

DD: We already have a lot of ways to refer to the global. Do we really need another one? Further (lower priority), there is a distinction between the global proxy and the global object. Setting properties on the global scope sets it on the global object, but referring to it gets the global proxy.

(Discussion of Domenic's concerns listed here: tc39/proposal-global#16 (comment) )

WH: How does this work across realms? The global object is a funny thing, as depending on where you evaluate it from, you may get different ones. If the global object is a proxy, is the system object also a proxy?

MM: The System object is very much like the Math object

WH: In an environment with multiple realms, there will be multiple System objects.

DD: (draws diagram to explain WindowProxy)

AK: Is this relevant to the System.global discussion?

JHD: I'll reiterate that this is not adding a new capability, as you can use the Function constructor; this just gives a way to do that without eval.

DD: Actually IE/Edge is restricted in a way that I don't understand for calling the function constructor in some ways

MM: This is one case where it's been hard for me to contribute to the web side of the standards world

BE: Ultimately this is about things like window.setTimeout (with a string so it will do eval)? Or what situations?

DD: Even executing a closure.

DD: WindowProxy is there so that, if you navigate a frame within another frame, the "window" object of that frame stays the same identity, so it is a "proxy" to an inner global object which changes on navigation. There's no way to access the inner object from within script.

JHD: Regardless of WindowProxy, System.global returns the global object, the same one that's accessed through 'this' or whatever else.

DH: Yes, this is the thing in the space of globalness that's relevant.

JHD: So we need a way to access the global 'this' value, a way that's the same across all environments. The function constructor can't be called from CSP. So we have two options--the bare 'global', or System.global. I haven't found anyone do typeof global

BF: Browserify's wrapper uses typeof global

JHD: But actually everyone uses 'typeof process' together with that when detecting Node-ness.

AWB: Two other options for names: Object.global

MM: I veto that as it would violate authority separation. I want to separate authority-carrying objects from non-authority-separating objects.

DD: But Object.__proto__ is Function, which gives you the authority to get the global object anyway.

MM: Function and eval are evaluators; CSP gives you the option to turn off evaluators. Any other environment such as SES/frozen realms has to intervene in the evaluators in order to constrain the scope of the evaluation. In any such environment, the function constructor has to be sanitized.

YK: Doesn't that mean that we don't currently have authority separation, and you already have to do some work for this?

MM: We've addressed current pain points; let's not add to the burden.

AWB: A lot of this seems to revolve around the function constructor. Another place to hang this would be Function.global.

MM: Let's not spend time on things that are strictly worse

JHD: Mark has argued that this cannot be a global variable so that it's more easily secured.

DH: Sounds like a good time to bring in the Realms API and the global scope contour, which introduce additional concepts that need work in the language. It's an open area for discussion, where we might want to expose the actual current realm.

AK: There's something special about global. There's already three ways to access this thing; adding new ways is sorta bad. We're not adding a bunch of things; this is just accessing one thing.

MM: Right now, none of the ways of accessing global are in the ECMAScript spec, and it's a beautiful thing that the boundary between the ES spec and the host corresponds very closely to user-mode computation and system mode computation. ES standardizes no way to cause effects on the outside world; the host provides bindings on the global object to cause effects on the outside world. Because the other things are put on the global, the global itself should be considered a host object that provides access to these things. Because there is no binding to the global object, nothing grants authority. There are several future proposals which do things like that.

AK: We don't have any of those. Global is special. There is some advantage to making the name short. Let's say it doesn't set a convention.

AWB: This should be a built-in module

JHD: So far, every time this came up, we said that we don't want to block it on this; once there's a standard modules proposal, we can consider this.

DD: We have a strong precedent for just calling this global. I want to understand your objection, Mark. It sounds like you're saying, let's not set the precedent of exposing a power object, when it's already exposed in reality in so many ways.

MM: Because it's not in scope in the ES spec, an environment could claim conformance to ES without exposing this.

DE: It is in the intersection of the various popular embedding environments, however.

JHD: I went with System.global because of the nonzero compat risk of bare 'global'. However, I haven't found any concrete compat risks. The second reason is that Mark thought it would help restrict authority to put it under a namespace.

YK: SES already has to care about this kind of thing.

MM: We currently have a whitelist in SES. global is not one of them.

JHD: So if you're OK with not adding a global, you don't have to do anything to SES?

MM: I'm concerned about the maintenance cost and initialization time. Spec changes cause SES to trail, which users find painful.

JHD: But sounds like SES users will be protected anyway

AK: The difference between other things considered to add to System, Loader, etc and global is that they add new capabilities, whereas global does not add a new capability. It's just exposing an existing thing. Therefore it doesn't need to be added in a particular namespace, and doesn't set a precedent.

MM: I hope that frozen realms will be added and hide the global object.

JHD: It sounds like the requirement that it be under a namespace is not very strong, simply a preference

MM: "simply a preference" is too weak. The issue of distinctions and psychological burden and precedent setting are pretty important.

DT: You're talking about the burden on developers?

JHD: I agree that they're real issues, but users already have the burden of having this reference.

YK: Given that you expose the global object, seems like it doesn't give a new capability.

MM: I'll have to think about this, but I can't approve that as a sound choice at the moment.

JHD: If Mark were to agree to Yehuda's point, if we changed the name to 'global', would anyone object to it with the name simply 'global'?

DD: I'd prefer 'self'

CM: no self

DD: Would prefer self

AK: 'global' seems fine. Most people don't seem to want 'self', Domenic.

YK, AK: This doesn't give any new capability

JHD: Sounds like the next steps are for Mark to think through Yehuda's objection and see if he can support bare 'global', and for me to follow up with Mark, Dean and <who?>

Conclusion/Resolution

  • Stage 2 holds

10.ii.b Template literal revision to stage 3

Tim Disney

https://tc39.es/proposal-template-literal-revision/

Reviewers: Mark Miller and Waldemar Horwat

TD: (recap) w/ tagged template literals, escape seq. illegal. Changes seek to allow

TD: Approval Stage 3?

Conclusion/Resolution

  • Stage 3 acceptance

9.i.i ECMA-402 formatToParts

Zibi Braniecki

ZB: Have implementation in SpiderMonkey, looking for a second implementor to proceed stage.

DE: To help formatToParts to Stage 4, ideal to upstream to ICU

CP: upstreaming to ICU should not be a requirement

AK: V8 not waiting for Stage 4

YK: The chicken-egg problem is about unflagging, not implementing.

ZB: Hoping Chakra, JSC, or any would implement

DD: What is the rush?

ZB: Not a rush, but the proposal has been ready for half a year. This is the last change that remodels the API. Everything else is new editions. This change opens abilities for cleanup.

YK: My opinion is that we've met the criteria

AWB: Do we have two implementations?

ZB: Yes

AWB: Compatibility issues?

ZB: Shipped in SM behind flag

CP: There are a lot of new Intl features that need formatToParts

DD: They can just be dependent

EFT/RW: That's bogus.

AWB: There is experience from significant usage? Want to hear about that

ZB: Used in consumer-facing product, Panasonic TVs running FirefoxOS. Proven that necessity to format is satisfied by this API.

AWB: With this exact proposal?

ZB: Yes

Implementations:

  • Intl.js
  • SpiderMonkey

ZB: The only feedback we've had is that developers want it in other browsers.

DE: Need to forward that feedback to other browser vendors

ZB: Want to plan for ES2017/402

RW: What is 262 cutoff?

BT/AWB: January/March (January for opt out, March for absolutely latest)

WH: I personally think you have enough implementation experience to advance to stage 4 now.

ZB: Can we advance to stage 4?

AK/DE: Some cases need 2 or more browsers, but this is not one.

Conclusion/Resolution

9.i.j ECMA-402 Intl.PluralRules

Zibi Braniecki

ZB: Can we advanced to stage 3?

ZB: Both reviewers (CP and DE) have finished the review!

CP: Thumbs up!

DE: The spec text is complete. No worries in terms of performance and memory allocation.

ZB: All data needed for PluralRules to function are already in place, and used by Number and Date, no extra data is needed.

Conclusion/Resolution

  • Stage 3 acceptance

10.i.a Require Unicode 9.0.0

Brian Terlson

Patch: Require Unicode 9.0.0 instead of 8.0.0

BT: We can update every year, or change language

EFT: "use latest"

AWB: Normatively referencing a spec with no version means "the latest"

BT: There are no changes in 9.0.0. Committed to reviewing changes each year.

RW: The 402 editor will have to do the same task

(agreement)

WH: New Unicode case mappings might cause \p{} (and especially ^\P{}) problems analogous to the \W problem we recently encountered.

BT: This won't be added in the future, but they can't be "fixed" because they can't break compatibility.

DT: Should the requirements be clear for ES implementations?

BT: risk with respect to breaking change updating Unicode, larger possibility with \p

  • Unicode is reluctant to touch case mapping
  • characters might move

DE: But these tend to be bug fixes?

BT: Still a risk of a breaking change

Conclusion/Resolution

  • Do not merge the PR
  • Use language for "the latest", i.e. unbounded reference
  • This should be applied to 402 as well

10.ii.c trimStart/trimEnd

Sebastian Markbåge

SM: change from "trimLeft" and "trimRight" to "trimStart" and "trimEnd". Put an "alias" in Annex B?

SM: Web compatibility risk: the names of the functions changes.

DE: We changed a separately named Date method to an alias in V8 and didn't experience any web compatibility concerns, so I wouldn't anticipate any web compatibility issues here.

SM: We have other aliases in the spec; I was trying to follow existing precedent

JHD: e.g., Array.prototype.values/[Symbol.iterator]

AWB: If there was evolution of mainstream functions, the web compatibility functions

DE: Code is likely not relying on the name, likely not much risk

SM: Need reviewers

Jordan Harband + Daniel Ehrenberg

AWB: Spec for trimLeft/Right is the same as trimStart/End?

JHD: Exactly the same, only a name change

MM: That's the breaking change?

JHD: Yes, otherwise there is no issue

Conclusion/Resolution

  • Stage 2 acceptance

Array.prototype.values

Adam Klein

AK: We tried shipping Array.prototype.values and it broke some random Microsoft property, with the "'values' in" pattern

BT: We shipped it

EF: We didn't ship it on Release, but it's on Beta.

AK: Safari shipped it, but it seems like people who depend on Chrome are not shipping it. There's a switch between an object with a values property and an array. This is a general problem with adding a property to existing objects.

YK: We could add a function to get the values out.

AK: Chrome is not going to ship this any time soon, as we tried a year and a half ago and it didn't work, and we tried now and it didn't work.

AWB: This isn't a web interoperability issue; it's a Chrome interoperability issue.

AK: I'm not so concerned about it because it's not used so often, but it might make people sad that it's not shipped.

AWB: This isn't a new thing in the world of JS, but eventually everyone converges to standards, see IE/Edge

AK: Please do not add any more things to Array.prototype

BE: ES4 tried to do namespaces. Modules should give us a way to do this. I'm agreeing, we can't really extend things too much.

RW: what software?

AK: CRM Dynamics 2013

BT: It is fixed, but people don't update their on-premises software. They hire a contractor to install it, and then the contractor walks away. We tell them just use IE11 rather than Edge. You can configure a group policy to open certain pages in IE11

Conclusion/Resolution

  • No change; Firefox will proceed with caution in shipping values.

10.iv.a Class Field Initializers: this semantics

Jeff Morrison

https://github.com/jeffmo/es-class-public-fields

JM: main issues

  • Contextual things in the initializer scope.

  • Initializers execute at the time of the last

  • static field initializers are executed as the last step of class definition evaluation, need access to the class name binding itself.

(discussion of the scope of initializers)

JM: What should be available inside the initializer?

  • Banning arguments, new.target (because unclear) -- these are static semantics errors when occurring literally, and undefined in direct eval
  • Allowing this and super property access

YK: It would be confusing to have the old enclosing copies of new.target or arguments inside of the initializers, which run later

AWB: Can't you write "arguments" in computed properties?

YK: We discussed this in the context of the cross-cutting class proposal with Brian

  • You might want this in a static property to refer to the class you just created

JM: There have been issues created in the Babel tracker about the scoping of this

AWB: In a static method this refers to the constructor function

  • The constructor function that the static property is defined on

AWB: A consistency for static initializers for the left and right hand side of initializers would be to be "the same as in a static body"

JM: open q: "Do we permit or ban new.target or arguments in the right hand side of initializers?"

YK: This becomes tricky because some people expect that a field referecnes ambient bindings, but it's easy to get confused if you treat the initializer as a method

AWB: These initializers are very shorthand for a function, which always has new.target

MM: In favor of banning

AWB: A use case is a static method on subclasses that filters the initializers, as called from a superclass constructor. In that case, you want to call |new.target.initializerFilter()|, and so want new.target to work

YK: Even though you can construct models which are consistent about which is which with new.target and arguments, it's equally hard to construct one that is unambiguously clear which one you are referring to

YK: This isn't a catchall; there will still be imperative cases that benefit from being written in the constructor

JM: If we ban them now, we can always add them later.

JM: We also need to decide what to do with eval('new.target')

MM: Presumably if it's an early error without eval, then it will throw early during the eval as well

(discussion of alternatives)

MM: Because this is a convenience tool, we can ban eval without loss of expressiveness. It's just a loss of convenience

DH: On instinct, this scares me. Just because we can't come up with a use case, doesn't mean that something won't ever come up

WH: How deep would the direct eval ban go? Expression, subexpressions, within arrow functions inside subexpressions, ...?

DH: banning direct eval is hard, because it's undecidable whether it's direct eval

YK: Yeah, we'd have to ban the entire class of things that could wind up being direct eval

MM; The only reason we're allowing these outside of the constructor at all, is for common well-behaved cases

AWB: Think of this as shorthand for function. If it was a function, all functions define new.target. When invoked you get undefined. Functions have arguments. There are no functions. If you just treat an initializer like an anonymous function, then you get exactly everything falling out

DE: If you treat it like just a method, then you get everything you want

AWB: This avoids a laundry list of special cases

KG: What about statics? The easy corollary is that for statics, it's an anonymous static method.

YK: This also allows for sane semantics for this

KG: (whiteboard)

class Base {
  prop = new.target.filter ? new.target.filter(val) : val;
}

class Derived extends Base {
  static filter() {}
}

AK: Does this restrict implementation's ablity to optimize away? No, we should be able to statically determine whether eval or arguments are possible.

EF: This is a simple front-end optimization.

Consensus: Initializers run with the semantics of individual anonymous methods with no arguments being run on the class. That explains the scope of this and super property access; new.target would be undefined, and each would get a new empty arguments object.

JM: Do properties get created with DefinePropertyOrThrow() or Set()?

(whiteboard)

class FooText extends TextNode {
  textValue = "asdlsk";
}

YK: Users would likely expect to get a Set; most users don't understand setters, and would expect that setters are always called

WH, MM: Would be really surprised by a Set in this example. The intuition is for initializers to use define.

JM: You could phrase this either way; either it's a hazard, or it sets the expectation that users can depend on a predictable DefinePropertyOrThrow.

WH: The reason why Set doesn't work is that it's not extensible to const, to typed properties, all of these can only be done on DefinePropertyOrThrow

textValue = "adadad"

^^^ Authors don't immediately recognize that the difference between Set() and DefinePropertyOrThrow()

WH: Can't use Set() to define const or typed properties. Only DefinePropertyOrThrow will work.

MM: A frozen data property, overriding with Set() will throw

JM: Having spent some time on this, I think neither is more intuitive than the other. People are very split on this. So as an argument for which is more intuitive, that's a wishy argument. We should go based on what is more useful.

AWB: If you define a method named textValue, you can't

BE: = looks like Set

AWB: So we should use colon

YK: Colon is reserved for type annotations

BE/WH: the only robust semantics is DefinePropertyOrThrow()

WH: Only DefinePropertyOrThrow will work across future extensions, for const properties and typed properties

  • evaluate the type, around time of initializer, can't create it and later change type, so it MUST be created with DefinePropertyOrThrow()

SP: How is this conflated?

MM: The idea that an initializer does a Set(), but a const does a DefinePropertyOrThrow() is just (confusing?)

MM: We already have in ES6 practice for how to initialize objects imperatively, with this.textValue = "asdf" inside the constructor. That's imperative, that does a Set(), it has all the properties that Yehuda is advocating. Any proposal that adds new syntax should, to justify the cost of the new syntax, have less overlap with something you can easily say already.

this.textValue = "...";

...is a Set()

YK: This is shocking. That means that you are saying we shouldn't have symmetry

MM: I value symmetry, but I don't see a symmetry argument. Leaving aside BE's point about the token, for object literals, all properties in object literals are initialized with DefinePropertyOrThrow

YK: But most people will have the imperfect but good-enough mental model to view this as a desugaring to this.textValue = ...

BE: This isn't true, as object literals already do something different compared to manipulating the object, if you put a setter on Object.prototype

YK: But that is suprising to many users

AK: What about developers of other languages who define fields and expect those to not trigger setters (as in other languages, where setters don't happen when you declare fields)

MM: This is just like an object literal. Writing object literals has the same problem.

YK: That's different because object literals don't have superclasses. People don't use __proto__: very much

BE: What's the robust, future-proof semantics? Seems like DefinePropertyOrThrow, as Waldemar says.

DE: If you subclass an HTMLElement and try to define an onclick, that would be fired either

JM: What about :=

SM: Having setters already be there is one use case. The primary use case is turning an existing property into a setter or getter, to shadow something else or make something lazy. If that's the upgrade path, then this will break.

DD: That's a good point; people do deprecations by adding setters or getters, and that would fail because it would just shadow

MM: If the semantics of this is Set, I would ask, what is the added value of the proposal as a whole such that the proposal pulls its weight. You could get these semantics just by writing the right lines in the constructor.

DE: Good from an optimizability standpoint. We've been encouraging people to be explicit about properties on this by adding them all in the constructor, assigning undefined if necessary. This is a nice syntax for that.

WH: Using Set() is dangerous, if derived class has foo, exposes you to a super class that didn't have accessor foo, which is later added.

YK: Lots of classes which are designed to be subclassed assume that they can intercept Set()s

DT: Initialization in C#, Java, etc is explicitly setting up the world and invariants aren't there

DD: For us it's different, because super has already returned, etc

DT: Using DefinePropertyOrThrow is good because it'll give us that capability from other languages, where you add the properties without invoking setters.

SP: In the current proposal (with Set), it's the same as doing the code in the constructor, so the constrained DSL is nice because it is not so order-dependent, right?

JM: Sounds like we won't resolve the issue in this meeting; I'll work with some people offline to come to more of a conclusion on this.

JM: The last open question: For fields that do not have an initializer, these implicitly initialize to undefined, not copying this.x = this.x as previously. I went back and forth on this; one of the reasons I was against this is that I was concerned about shadowing the same-named property on the parent class. But what convinced me is that, presumably, if you put that declaration there without an initializer, you would intend to initialize it later at some point, in which case you would be shadowing the parent property anyway. So the expectation is that, when you put the field there, you will be shadowing the field above it. So the initialization happening in order makes sense.

class FooText extends TextNode {
  textValue; // textValue = undefined;
}

WH, AWB: Good.

AWB: In lexical scoping, we decided that the same name in the same scope would always refer to the same binding. The analogue here points in this direction as well.

JM: Cases where it's useful to define the value of a child in terms of the parent property value...

this.x = this.x + 1;

AWB: Suggest creating the fields first, presumably undefined, then assign.

WH: Separating creation from initialization is not future compatible with const or typed properties either unless we want to do something equivalent of a TDZ for properties, which I don't.

WH: Back to JM's original question, the only sensible thing for a field declaration without an initializer to do is to create the field with the initial value undefined.

DD: left hand side of computed property evaluates once (for definition), the right hand side many times (for instantiation)

[this.counter] = this.counter++;

JM: concern about syntax, further syntax might help solve, will consider

AWB:

DD: It should be consistent with fields

JM: I see the analogy with methods as being stronger. Anyway, most use cases will be with Symbols.

AWB: Want consistency across the whole construct, whether method, field or getter/setter

AK: This isn't thought of as a statement, but rather as a declaration, so the evaluation of the computed property names goes with the other declarations

(discussion re: transpilers)

DH: "identifier : identifier" currently means type annotation

with respect to this ^^ https://tc39.es/ECMA-262/#sec-forbidden-extensions:

The Syntactic Grammar must not be extended in any manner that allows the token : to immediate follow source text that matches the BindingIdentifier nonterminal symbol.

JHD: Babel is doing fine with Set, this scoping, etc, and we should take that as a datapoint. No user complaints.

MM: Should the properties be defined as configurable or non-configurable?

JM: Currently it creates a non-configurable property with DefinePropertyOrThrow

YK: Seems strange why we would do it differently from methods

MM: I strongly prefer non-configurable. We already have an imperative syntax for imperative initialization, properties that are configurable and disappear. The time-dependent nature of configurable properties don't give you as many guarantees. Non-configurable properties let you minimize gratuitious differences with private state and get static analyzability and static shape. The behavior of private fields is more like a non-configurable property than anything else.

AWB: I support your position, but in addition: one of the advantages of this new syntax, is that it allows to declaratively define the shape of the object.

  • Value in readability
  • Make non-configurable is a powerful value

AWB: what happens when a super class has already defined one of these? When they are configurable?

MM: Silent replacement

  • Prefer: two declarations of the same own property, an error.
  • Set() alone doesn't produce an error, DefinePropertyOrThrow() alone doesn't produce an error.

DE: super constructor might return a Proxy

EFT/AK: Implementors can't infer more about shape than before

JM: Objections to Stage 2?

AWB: I want to see this and private fields merged. I don't think we should advance either on their own to stage 3.

Conclusion/Resolution

  • Stage 2 acceptance
  • Initializers run with the semantics of individual anonymous methods with no arguments being run on the class. That explains the scope of this and super property access; new.target would be undefined, and each would get a new empty arguments object.
  • No consensus on Set() vs. DefinePropertyOrThrow()
  • A field with no initializer should create the property with the value undefined
  • No consensus on configurable vs non-configurable
  • Revisit initializer token choice of =

Ben Newman

BN: Potentially relaxing restriction that import can only appear at the top level.

BN: proposal would allow imports that are nestable; hoisted (declarative over imperative); lexically scoped; sync eval'd; back compat.

BN: We have been shipping this to the Meteor community in a transpiler context. The semantics are more subtle than you would expect to allow, due to the subtleties of modules. They are synchronous (hoisted to the top of the scope) and backwards compatible.

MM: [clarifying that bindings are immutable on the import side]

YK: [clarifying that currently proposal is transpiled to commonJS require]

... discussion about the spec; request to skip the spec discussion at this stage ...

AWB: How about we consider these to be ordinary lexically scoped variables?

WH: If I sit inside a loop, importing a module mutiple times, will I get the same module namespace object?

BN: Yes. In subsequent iterations, it doesn't get imported again

WH: Is the module specifier still restricted to only being a string literal?

BN: Yes.

DH: In this proposal, no matter what, all imports will be loaded beforehand. It's only that evaluation will be deferred until it reaches that point in the code.

AWB: Do you hoist evaluation as well as instantiation? - I would expect hoisting, for consistency with top-level imports

BN: the ability to have nested imports gives you a tool to control relative execution of import declarations to other statements

WH: Yes, that's the Optimistic Imports example from the proposal.

YK: DH's point is important. If you want predictability, you do not want interleaving.

BN: Modules get all loaded before the program starts. Their instantiations are done as their blocks are encountered in the program.

WH: So then what happens if a module is missing when being loaded? How do Optimistic Imports work?

BN: necessarily an error if it fails to load.

BN: ... talking about a use case for cross-env code with conditional imports ... if it turns out that the fetching of the module source fails, it is entirely conceivable that the dev knows this was a possibility and that that import is inside a conditional block that prevents it from evaluating - so I think it's less fatal for the fetch to fail.

YK: an important use case for this is "if I'm in env X, import stuff for env X"

DH: proposal: we unconditionally do all the fetches, but if any fail, we don't actually fail - just hang onto the error for later. and only if control flow reaches the point where it's needed, does it end up being an error. iow, if you only have conditional imports, failures will not create a compile-time error, and only create a runtime error when the module is needed and missing.

AWB: consistent with the current spec.

AK: changing what used to be early errors into not-early errors.

AWB: I mean consistent in that it treats each block as if it's its own module.

YK: ?? conditional importing means the dev knows it's benign if it's missing, and can work around that.

BN: ... explaining "isolated scopes" example ... ability to reuse identifiers in the same file, but in separate scopes. With top-level, you need slight identifier variations.

SP: also refactoring hazard - if the imports are in the describe, it's easier to split up large files without breaking things.

BN: in top-level model you still have to do all the same fetching

CP: at what point in this program is the error triggered? we intentionally chose declarative import evaluation as well as initialization/loading.

BN: the specific "it" test in my example will fail if the import nested inside it fails to load.

BN: it should fail every time its import is attempted

DH: it should be possible via the registry to clear things out and make it possible to retry, but only in a staged way - ie, you have to kick off an evaluation, not in the middle of a module.

BF: in node, if it evaluates, it's put into the cache; if it fails to evaluate, it's not put into the cache.

... discussion about entire dep graph being resolved before any evaluation ...

DT: ... general argument that nested imports would have to be VERY compelling to make it worth it ...

AWB: what about imperatively using a dynamic import API?

DH: loader API does not let you statically express dependencies, so we talked about a syntax that allows dynamic async importing

... more discussion ?? ...

DH: inherent conflict with top-level await

BN: I think the dynamic syntax you're proposing is essential for top-level await. what would happens with current import if you pull in a module using top-level await?

CP: top-level import would handle it

DH: essentially forks the world - ie, "modules that can use top-level await" and "modules that can't"

BN: still some motivating examples worth talking about

BF: a point: top-level await is going to pretty much wreck node's sync loading anyways

BN: if top-level await turns out to be infeasible, then the conflict here is moot, but still worth talking about

YK: my concern is that it makes loading unconditional + evaluating conditional, and i think conditional loading is important too

BN: skipping over "lazy eval" claim. will note that in node, which has sync i/o, pushing requires into functions has helped startup perf (likely from deferring the i/o)

SP: depends on the complexity of the file you're importing

SM: biggest perf problem at FB is parsing and initialization. we load everything and only evaluate the things we need. we can't switch to modules without lazy eval + lazy loading.

BN: i think it will not interfere with lazy loading.

BN: if you treat all module identifiers the same, then you don't have to parse the whole file, you just have to tokenize it and look for "import" tokens.

EF: i think you grossly underestimate the costs involved.

... discussion about tokenizing vs parsing; costs thereof; etc ...

BN: in my naive understanding of tokenizing and parsing, you could tokenize, iterate, locate import, locate the possible path - then optimistically parse that path whether it's a module or not.

AK: unless you're talking about throwing away all module early errors, you need to know the exports, so you still have to parse. your approach only would allow easier fetching.

BN: if i'm wrong about the tokenizing trick, it still doesn't matter where the import declarations are

YK: i think the point is that if it turns out that conditional import ...

SM: what matters is "do you do the fetch if there's a syntax error"

CP: Do we need to have a user make a change to allow for this tokenizing optimization?

BN: The optimization is orthogonal

DH: He means deferring evaluation

BN: That seems unsafe to me.

BN: Colocating imports and code should be good long-term for code hygiene because it should keep the things together and help you as you move the code around. And of course you can keep putting them all at the top level if you want.

BN: Further justification: Put an import in a try/catch block in case you have fallback behavior.

SP: But what if it's not the first time? This scares me. You don't get the chance to execute your fallback behavior.

MM: Maybe you should remember the throw and rethrow if it failed.

CP: You were saying that it should go to the top

BN: To the top of the block. It will still throw from within the try block.

AK: To me, this looks like a nested module. It seems like it would be a module in the tree which fails.

BN: Another benefit is dead code elimination--you can avoid running the code by importing only when needed, and this then doesn't run the top-level statements.

AK: Dead code elimination will be approximate in JavaScript. It couldn't be done by implementations, but could be done by preprocessors which are OK with changing semantics.

MM: Is it valid to eliminate the fetch?

BN: Technically these change semantics, but may be OK for the application.

BF: Module resolution may not include fetch; for Node, things will be pre-populated

YK: We did a lot to support the web's asynchronous fetching.

BN: Yes, we needed to support browsers. Nested import declarations seem problematic mostly because of the browser and asynchronous fetch.

BN: I have a grammar in the proposal. Import is not allowed in non-modules. There is a PR, I invite people to participate there. Nested imports from scripts are prohibited with static semantics.

BN: We have settled on declarative imports, hoisting them. But nested imports give you a tool for doing imperative imports, though no deferred fetching and specifiers are literal

BN: Stage 1?

DH: Suggested area of exploration: await import

async fn() {
    await import {x} from m;
}

AWB, YK: We should merge this group with the loader group to come back in a few months.

AK: I'm surprised you're not upset about it being a promise.

DH: But the Promise is not reified

BN: I would prefer synchronous semantics, if possible, but we could live with this. For one, it would be nice to run successive import lines in parallel.

Conclusion/Resolution

  • Stage 0 holds.
    • A synthesis proposal will be pursued at a future meeting.