14
Operators and Literals Brendan Eich [email protected] @BrendanEich Ecma TC39 May 2015

Extensible Operators and Literals for JavaScript

Embed Size (px)

Citation preview

Page 1: Extensible Operators and Literals for JavaScript

Operators and LiteralsBrendan Eich

[email protected]@BrendanEich

Ecma TC39 May 2015

Page 2: Extensible Operators and Literals for JavaScript

Value Types Review

• Int64, Uint64 (note capitalized names)

• Int32x4, Int32x8, etc. (SIMD)

• Float32 (to/from Float32Array today)

• Float32x4, Float32x8 (SIMD)

• Bignum

• Decimal (long-time TC39 goal for IBM: self-hosted)

• Complex

• Rational

Page 3: Extensible Operators and Literals for JavaScript

Value Types Review, 2

var ColorType = ValueType(Symbol(“Color"), // or Symbol.for? {r: Uint8, g: Uint8, b: Uint8, a: Uint8});

ColorType.prototype.average = function() { return (this.r + this.g + this.b) / 3;};

var color = ColorType({r: 22, g: 44, b: 66, a: 88});color.average() // yields 44

assert(typeof color == “color”); // un-capitalized!

// ISSUE: some web JS hardcodes known typeof results

Page 4: Extensible Operators and Literals for JavaScript

Literal Syntax

• Int64(0) ==> 0L // as in C#

• Uint64(0) ==> 0UL // as in C#

• Float32(0) ==> 0f // as in C#

• Bignum(0) ==> 0n // avoid i/I

• Decimal(0) ==> 0m // or M, C/F#

• Lexically transpose, e.g., 0L into L(0)

• Sanity: map short suffix to constructor name

• Reflect.defineSuffix(‘L’, Int64)

• or literalSuffixTable.L(‘0’) h/t @littledan

• or use imported names only? h/t @sebmarkbåge

Page 5: Extensible Operators and Literals for JavaScript

Overloadable Operators

•| ^ &

•==

•< <=

•<< >> >>>

•+ -

•* / %

•unary- unary+ boolean-test!! ~

• ISSUES: lost invariants in spite of overloadable subset

Page 6: Extensible Operators and Literals for JavaScript

Preserving Boolean Algebra

• != and ! are not overloadable, to preserve identities including

• X ? A : B <=> !X ? B : A

• !(X && Y) <=> !X || !Y

• !(X || Y) <=> !X && !Y

• X != Y <=> !(X == Y)

• ISSUE: implicit-!!(x) vs. !(!x)

Page 7: Extensible Operators and Literals for JavaScript

Preserving Relational Relations

• > and >= are derived from < and <= as follows:

• A > B <=> B < A

• A >= B <=> B <= A

• We provide <= in addition to < rather than derive A <= B from !(B < A) in order to allow the <= overloading to match the same value object’s == semantics

• And for unordered values (NaNs)

Page 8: Extensible Operators and Literals for JavaScript

Strict Equality Operators

• The strict equality operators, === and !==, cannot be overloaded

• They work on frozen-by-definition value objects via a structural recursive strict equality test (beware, NaN !== NaN)

• Same-object-reference remains a fast-path optimization

Page 9: Extensible Operators and Literals for JavaScript

Why Not Double Dispatch?

• Left-first asymmetry (v value, n number):

• v + n ==> v.add(n)

• n + v ==> v.radd(n)

• Anti-modular: exhaustive other-operand type enumeration required in operator method bodies

• Consequent loss of compositionality: Complex and Rational cannot be composed to make Ratplex without modifying source or wrapping in proxies

Page 10: Extensible Operators and Literals for JavaScript

Cacheable Multimethods

• Proposed in 2009 by Christian Plesner Hansen (Google) in es-discuss

• Avoids double-dispatch drawbacks from last slide: binary operators implemented by 2-ary functions for each pair of types

• Supports Polymorphic Inline Cache (PIC) optimizations (Christian was on the V8 team)

• Background reading: [Chambers 1992]

Page 11: Extensible Operators and Literals for JavaScript

Binary Operator Example

• For v + u with either a value type instance:

• Let p = v.[[Get]](@@ADD)

• If p is not an OperatorSet, throw a TypeError

• Let q = u.[[Get]](@@ADD_R)

• If q is not an OperatorSet, throw a TypeError

• Let r = p.join(q) (r an Array)

• If r.length != 1 throw a TypeError

• Let f = r[0]; if f is not a function, throw

• Evaluate f(v, u) and return the result

Page 12: Extensible Operators and Literals for JavaScript

Reflect API Example

function addPointAndNumber(a, b) { return Point(a.x + b, a.y + b);}

Reflect.defineOperator('+', addPointAndNumber, Point, Number);

function addNumberAndPoint(a, b) { return Point(a + b.x, a + b.y);}

Reflect.defineOperator('+', addNumberAndPoint, Number, Point);

function addPoints(a, b) { return Point(a.x + b.x, a.y + b.y);}

Reflect.defineOperator('+', addPoints, Point, Point);

// NB: Calling defineOperator with 3 args defines unary operator

Page 13: Extensible Operators and Literals for JavaScript

Subclassing Problem

• Given class A and class B extends A,

• Reflect.defineOperator(‘+’, addAA, A, A);

• Reflect.defineOperator(‘+', addBB, B, B);

• a1 + a2 must select addAA

• a1 + b2 and b1 + a2 must select addAA

• b1 + b2 must select addBB not addAA

• How should OperatorSet implement this?

Page 14: Extensible Operators and Literals for JavaScript

Subclassing Solution

• OperatorSet stores (depth, method) pairs

• Reflect.defineOperator(‘+', addBB, B, B); copies B’s initial @@ADD and @@ADD_R operator-sets from A’s @@ADD and @@ADD_R sets

• OperatorSet.join, on finding intersection not a singleton, breaks tie by maximum prototype depth: (2, addBB) > (1, addAA)