March 22/23 notes

# Waldemar Horwat (14 years ago)

Here are my raw notes from the last couple days.

Ask the GA for a way for non-members to sign software contribution agreements? Waldemar: Thinks this would be a hard sell in the GA. They'll be annoyed at increasing provisions for non-members to participate. Istvan: This should not be "too innovative", at least until we get a lot more experience. ECMA should not directly accept third-party contributions. An ECMA member should serve as an intermediary. Allen: This is already coming up in the context of Test262. Third parties are coming up with tests and we would like those tests. John: Are the third parties willing to assign the code to one of the ECMA members?

Binary data: Typed arrays != ArrayTypes. DaveH is proposing ArrayTypes but not ArrayTypes. In practice there is little difference between them. ArrayType(int16) is drop-in replacement for Int16Array. DaveH is proposing ArrayBuffers (not currently on wiki). Every instance of an ArrayType or StructType (instance that contains actual data, not the type object itself) is also an ArrayBufferView.

Discussion of whether the raw buffer should be exposed for ArrayTypes created for in-program (non-i/o) use only. With this view, any client can extract the buffer and must get a specified view (big endian by default). Allen: Ability to extract the buffer from "new new ArrayType(int32, 100)" forces implementations to store it as big-endian even if they would rather not. This impedes performance.

Slide with: MyType MyType(ArrayBuffer buff, uint32 idx = 0, uint32 length = buff.byteLength - idx, boolean networkByteOrder = true); Waldemar's misinterpretation of the bool: networkByteOrder = true means big endian. networkByteOrder = false means localByteOrder, which can be big or little endian, depending on local hardware. To avoid such interpretations, if this bool is meant to distinguish between big and little endian, call it bigEndian.

Brendan: What if we don't allow aliasing of multiple-sized interpretations for the same data? i.e., once something is an int32, can't access it as int16 or bytes? Waldemar: This would break the common case of creating structures that contain crc's or digital signatures of parts of their content. MarkM: Any reason to allow a fuzzy (rather than explicitly specified) byte order for such cases? Waldemar: No.

Waldemar: What's the practical user-visible difference between ArrayBuffers and Blobs?

Alignment: Atomic types within a struct must be naturally aligned. Struct lengths must be naturally aligned to the largest data member.

What about unaligned use cases? These are fairly common in file formats. Most processors have some way to access unaligned types which is substantially faster than extracting single bytes in ECMAScript and shifting, so we'd want to allow it in some form.

DaveH: Solution: Packed types vs. unpacked types.

Luke: Fall back to data views if structs don't solve a problem well.

Waldemar: Most files contain lots of string data in various formats (UTF-8, UTF-16, ASCII, etc.). The complete lack of string support is the major obstacle to using this framework to parse files. User programs will have to parse UTF-8 and construct strings byte-by-byte (which might be O(n^2) on some implementations if accumulating a string by concatenation.)

Consensus on moving what's currently on the wiki page into harmony, with ArrayBuffers and strings as important goals for additions.

Doug: Concerned that we started with just moving data to GPUs but are now expanding scope into the much larger issues of file i/o. Allen: Why not make a separate task group to standardize just this?

Module loaders: Waldemar: evalLoad: Is it always asynchronous? Yes, but should discuss this on a different thread. Example: l.load("my-module-url", myCallback); // The module at my-module-url references global g l.defineGlobal("g", 42); Note that mere compilation of my-module must not begin until after the main thread completes; otherwise it wouldn't see the global "g".

l.defineGlobal("f", function() {...}); has the same behavior as: var f = «function»; except that «function» is created in the global context of the caller of defineGlobal, not l's global context. «function» is then bound as l's global variable "f".

Waldemar, Brendan: resolver.resolve/load/eval don't work well with redirects. You want to use the target of the redirect as the cache key, but by then you're in load and it's too late to do the resolve cache lookup. Origin policies also use the target of the redirect.

Custom loaders: when base.Array is not the usual Array object, what happens when evaluating an expression like []? DaveH: Core of the object is the standard Array, but the mutant Array constructor gets to run too. Waldemar: What exactly does that mean? Allen: Emphasis on constructors is misplaced. Other contexts are also important. Waldemar: Agree. Suppose you swap Array with RegExp. RegExp methods refer to hidden magic properties that are not on standard objects. They work and stay hidden because construction is coordinated behind the scenes with the other methods. MarkM: Make base optional in loader.create.

Move Modules to proposal status? Doug: Modules are one of the most important features, but too early to move it to proposal. Debate over what "proposal" means. Doug withdraws objection.

Quasis Q&A: Q. Do we have plans to standardize functions like safehtml? A. No, we will not standardize the functions. Q. How does safehtml decide when quoting a message like "<b>bold</b>

word" into HTML whether to keep safe tags such as <b> or whether to

escape them into <b>? There are plenty of use cases for both

situations and neither dominates. A. Plain strings are assumed to be raw text. To avoid escaping things like HTML you need to pass in a parameter of a special type other than string. Debate about whether quasis are a lexical or syntactic grammar production. They're lexical and deliberately don't include an expression subgrammar for SubstitutionBody. The text is lexed via SubstitutionBody and later parsed. Concern about grammar complexity of substitutions, particularly with nested `'s. Brendan: Why currying and thunking and assignable substitutions and all this complexity? None of the use cases (other than Decomposition Patterns, which also use setters) require thunks. Brendan: By default simply interpolate the components if no function is specified. Lively debate on the default. Doug: This will bring back security holes. DaveH: Simple concatenation useful for logging. DaveH: It would be good to have an HTML sanitizer in ECMAScript, just like how we have encodeURI, regardless of who standardizes it. Waldemar: Often want to refer to properties of a late-bound object, not the local variables. See internationalization example below.

Concern about lack of ability to indirect, as needed by, for example, internationalization. How do you obtain messages from a library and pass them indirectly into code? Each message captures variables at the point where it syntactically lies in the program, but here we want to late-bind the parameters.

To do this, you'd need to write tables of: English: function hellomsg(o) { return msgHello, ${o.world}; } function basemsg(o) { return msg'All your ${o.base} are belong to us"; }

French: function hellomsg(o) { return msgBonjour, ${o.world}; } etc.

Status? Brendan: "Sounds like this needs another run through the body shop."

Infix operators: ?? Replace undefined with a different value Do we want to also change the default parameter syntax to use ??= instead of = ? Metadiscussion about complexity budget.

has mod div min max

Syntax is compatible with ES5 (as long as there is a [no linebreak here] before the keyword) but precludes some considered extensions such as attributes decorating definitions. Debate about reverse direction of "has" vs. "in". Brendan: Not clear if "has" is worth it. "mod" produces the same sign as the divisor (as opposed to %, which produces the same sign as the dividend). How is "div" different from a division followed by truncation towards zero? They're almost the same, but the intermediate division can round up to the next integer if it's within 1/2 ulp of an integer, while div wouldn't. Brendan: Sympathetic to "mod" and "div" because the versions that people should use are obscure or wordy. Why not ** for power? "min" and "max" feel weird as infix operators.

Number.prototype.compare(v, t): |n - v| < t? Waldemar: This is the wrong comparison in many cases. Often you want to do a relative one. There is no single such comparison that would work in a majority of cases.

Number.EPSILON: nextdouble(1.0) - 1.0 Number.MAX_INTEGER: 2^53 exactly. It's the largest double d such that d - (d - 1) = 1. Complaints about the name MAX_INTEGER. It's not the maximum representable integer; in fact, every finite double greater than MAX_INTEGER is also an integer.

Various math functions on spreadsheet: Some are commonly used (hyperbolics, exp2, erf, erfc, gamma). Some are useful to avoid cancellation (e^x - 1 near x = 0). Some are useful fp format manipulations (nextafter, split into sign/mantissa/exponent and back). Most of the rest are esoteric.

Object initializers: Is the thing created by [<proto: myProto>, "a", 2, false] an Array (by

isArray) or not? Yes. Syntax collision with E4X. Brendan: Don't like comma after >. Too easy to miscount number of elements.

Waldemar: Like comma after >. Without comma, the above example would

look like a greater-than expression myProto > "a".

Method declarations within object literals: Are those allowed in arrays? No. You can't make an array literal with its own toString method.

Object literal property modifiers: p: value p const: value var p: value var p const: value sealed p: value sealed p const: value sealed var p: value sealed var p const: value method p() {} sealed method p() {} set p(v) {} var get p() {} sealed put p(v) {} sealed var get p() {}

Waldemar: Why is "var" a prefix but "const" a suffix? Too confusing. The property name p should be in a consistent place (preferably last because that's existing usage for getters). Brendan: "var" and "const" in the same property declaration? "var" does not intuitively mean "dontEnum". Brendan: configurable=true, writable=false combination not necessary? Waldemar: Since any keyword is usable as a property name here, what does this do? var const:value Is it defining property "var" with attribute const, or property "const" with attribute var? What if someone makes a typo and forgets the property name? Many of the above forms will declare a property named "var", "const", etc.

Private names in initializers: Controversy about 'private' capture semantics, orthogonal to object initializer syntax.

class c { method m() {return this.a+this.x} [HERE] new (a) { a: a, {this.x = somebody(this)} } }; Controversy about what punctuation goes into the place marked [HERE]. If classes are based on object initializer syntax, there has to be a comma there. Brendan: users won't expect that.

class D { <superclass: C, frozen>, new (a) { <closed>, a: a, {this.x = somebody(this)} } }; Note that the closed property is applied after the initializer block is run.

Debate about class private vs. instance private vs. ergonomics of protection by scoping. Allen: Private must satisfy:

  • Can't reflect on it
  • Proxies can't trap them
  • Don't need to stick lots of closures into each instance of a class just to close over private members. Dave: Some of these can be relaxed.

Discussion about how the private mechanisms here subsume some of the internal browser ones used to implement the DOM.

Initializers can dynamically created @-variables simply by assigning to them.

Waldemar: This is a problem:

class D { private iv1; ... new(a) { method m(v) {@iv1 = v;} } } my_d = new D;

class Evil { new(a) {{my_d.m.call(this, 33);}} } Evil gets to create @iv1 properties on its own objects without D's knowledge.

Allen: To fix this problem, disallow dynamic creation of @-properties from constructor initializers.

Discussion about preventing methods from working on fake instances.

Dave: var kye = Name.create(); function Thing(amIBlue) { this[key] = amIBlue; } Thing.prototype = { meth: function() { return this[key] ? "blue thing" : "red thing" } }

var evil = Proxy.create({... get: #(key) {...} ...}); thing.meth.call(evil);

Dave: Every time a private name gets passed to a proxy, the name gets wrapped. (to what?)

private dance; Thing.prototype.dance = fun() {...}

{ private α;

function Complex(r, i, α) { this.real = r; this.imag = i; this.α = α; }

function setα(α) { this.α = α; }

Complex.prototype.equals = fun(other) { return this.real == other.real && this.imag == other.imag && thia.α == other.α; } }

Now you can again create fake Complex objects by creating an object with real and imag properties and introduce an α property with setα. This can fool the Complex equals method.

Debate about branding.

Brendan: Should instance variables be non-properties (i.e. not inherited from protype even if they don't use private names)? Long debate of cognitive load costs and other attributes pro and con.

Date example: Users inherit from Date, but that breaks because Date objects contain a hidden time field, which inherited ones don't. If that field were a private name, this would work. But how would it work? A possible scenario would be that all BrendanDate objects (derived from Date) share the same hidden time field, in which case changing one would affect the others. To fix this, BrendanDate objects should call own-property methods, but then there's no inheritance. How would initialization of the hidden field work?

Allen/DaveH: Private names proposal on wiki will be modified to support strong encapsulation. Split proposal into runtime semantics and syntax/scoping halves.

Open-ended discussion of many aspects of this proposal. Not sure how to summarize it. It's also unfortunate that Mark isn't able to present his proposal due to an emergency.