Community-driven design:
|> in TC39
Daniel Ehrenberg @littledan
Igalia, in partnership with Bloomberg
TC39 delegate
WorkerConf 2018
Method chaining:
Awesome, right?
const cities = [
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
{ name: "Valencia", population: 790000 }
];
const cities = [
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
{ name: "Valencia", population: 790000 }
];
cities.filter(city => city.population > 1000000);
===>
[
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
]
const cities = [
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
{ name: "Valencia", population: 790000 }
];
cities.filter(city => city.population > 1000000)
.sort((a, b) => a.population - b.population);
===>
[
{ name: "Barcelona", population: 1609000 },
{ name: "Madrid", population: 3166000 },
]
const cities = [
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
{ name: "Valencia", population: 790000 }
];
cities.filter(city => city.population > 1000000)
.sort((a, b) => a.population - b.population)
.map(city => city.name);
===>
["Barcelona", "Madrid"]
OK, let's make our own!
function sortByPop(arr) {
return arr.sort(
(a, b) => a.population - b.population);
}
sortByPop(cities.filter(
city => city.population > 1000000))
.map(city => city.name);
Array.prototype.sortByPop = function () {
return this.sort(
(a, b) => a.population - b.population);
}
const cities = [
{ name: "Madrid", population: 3166000 },
{ name: "Barcelona", population: 1609000 },
{ name: "Valencia", population: 790000 }
];
cities.filter(city => city.population > 1000000)
.sortByPop()
.map(city => city.name);
Combining modules
cities.js
// Highest population cities
Array.prototype.sortByPop =
function () {
return this.sort((a, b) =>
a.population - b.population);
}
export function bigCities(cities) {
return cities.filter(city =>
city.population > 1000000)
.sortByPop()
.map(city => city.name);
}
combined.js
import { bigCities } from "cities.js";
import { manyFans } from "music.js";
// CLASH
music.js
// Most popular musicians
Array.prototype.sortByPop =
function () {
return this.sort((a, b) =>
a.popularity - b.popularity);
}
export function manyFans(musicians) { … }
function sortByPop(arr) {
return arr.sort(
(a, b) => a.population - b.population);
}
sortByPop(cities.filter(
city => city.population > 1000000))
.map(city => city.name);
function sortByPop() {
return this.sort(
(a, b) => a.population - b.population);
}
const cities = // ...
cities.filter(city => city.population > 1000000)
::sortByPop()
.map(city => city.name);
function f(x) {
// ...
}
// Bind operator
obj::f(x)
===>
f.call(obj, x)
Maybe soon!
We are looking into it
Pipeline/bind is at Stage 1
// ES6 Destructuring Assignment
let x = 1, y = 2;
[y, x] = [x, y];
⇒ x === 2
y === 1
Who is TC39?
● A committee of Ecma, with…
● JS developers
● Implementers
● Frameworks
● Academics
● etc
Meetings
● Every two months
● For three days
● Summarize/review GitHub activity
● Move proposals through stages
TC39 stages
● Stage 1: An idea
● Stage 2: Agreed-on first draft
● Stage 3: Details worked out
● Stage 4: Battle-tested and standard
History of ::
● Discussed :: in 2012 and earlier
● Developed by Dave Herman, Kevin Smith
● Implemented in Babel, 2015 by Ingvar Stepanyan
● Deferred until post-ES6
this ??!!?!?!?!
library.js
export function doubleSay(str) {
return str + ", " + str;
}
export function capitalize(str) {
return str[0].toUpperCase() +
str.substring(1);
}
export function exclaim(str) {
return str + "!";
}
with-pipeline.js
import { doubleSay, capitalize, exclaim }
from "library.js";
let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
// ===> "Hello, hello!"
ordinary.js
import { doubleSay, capitalize, exclaim }
from "library.js";
let result =
exclaim(capitalize(doubleSay("hello")));
// ===> "Hello, hello!"
History of |>
● Gilbert created |> repository, November 2015
● Reached Stage 1, September 2017
● Implemented in Babel, late 2017
by Henry Zhu, Justin Ridgewell
● Pursue two paths, March 2018
Async/await integration
Example
function doubleSay (str) {
return str + ", " + str;
}
function capitalize (str) {
return str[0].toUpperCase() +
str.substring(1);
}
function exclaim (str) {
return str + '!';
}
...the following invocations are equivalent:
let result = exclaim(capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
result //=> "Hello, hello!"
Evolving to async...
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(capitalize(
doubleSay("hello")));
result //=> [Object Promise]!
let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
result //=> [Object Promise]!
Oops, how can we fix it?
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(await capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = ("hello"
|> doubleSay
|> capitalize)
.then(exclaim);
result //=> resolves to “Hello”
Or, this would also work
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(await capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = await (
"hello"
|> doubleSay
|> capitalize)
|> exclaim;
result //=> "Hello, hello!"
A prettier solution (PR; proposed for C#)
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(await capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = "hello"
|> doubleSay
|> await capitalize
|> exclaim;
result //=> "Hello, hello!"
However, this would be different…
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(await capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = "hello"
|> doubleSay
|> (await capitalize)
|> exclaim;
result //=> [Object Promise]!
Another nice solution
function doubleSay (str) {
return str + ", " + str;
}
async function capitalize (str) {
let response = await
capitalizeOnTheServer(str);
return response.getCapitalized();
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(await capitalize(
doubleSay("hello")));
result //=> "Hello, hello!"
let result = "hello"
|> doubleSay
|> capitalize
|> await
|> exclaim;
result //=> "Hello, hello!"
Plan from here
● Pursue two options ("F#" and "smart pipeline")
● Write specifications and documentation
● Implement both in Babel, different flags
● Develop community consensus on F#, smart, and ::
● Propose a choice to TC39
Steps needed for Stage 2
● First draft spec text ✔
● Decide which variant …
● Agreement that we want the proposal …
Steps needed for Stage 3
● Complete spec text …
● Signoff from reviewers, editor …
● Ready to pass off to native implementers …
Steps needed for Stage 4
● Two implementations …
● Conformance tests …
● PR with editor signoff …
Getting involved
Feedback on GitHub issues
● Does this work for you?
● Missing pieces?
● Handle a case differently?
Pipeline GitHub feedback
● Proposal scope
● :: vs |>
● Await integration
● Avoiding currying
● Semantic details
Test262 tests
● Tests shared between JS engines
● “If it’s not tested, it’s not compatible”
● Finds edge cases
Pipeline test262 tests
● ...None yet
● Could be useful for Babel development
● Wanna help?
Documentation/Education
● Explain the feature to contributors, learners
● Understand mental models for JS
Pipeline documentation
● "Explainer documents" for each variant
● Wanted: Better introductory materials
● Wanted: Educators' perspective on the alternatives
Implementation
● Open-source projects accepting contributions:
○ Babel
○ TypeScript
○ V8
○ Acorn
● Features developed behind a flag/in a plugin
○ JSC
○ ChakraCore
○ SpiderMonkey
○ And many more
Pipeline implementations
● Babel transforms
● Want to write more?
Attending TC39 as a delegate
● Join Ecma to come to TC39 meetings
● Contact me for more info, littledan@igalia.com
● Get involved in TC39:
https://tc39.github.io/
● |> is at Stage 1
for compositional chaining
● Daniel Ehrenberg
● Twitter/GitHub @littledan
● littledan@igalia.com

Community-driven Language Design at TC39 on the JavaScript Pipeline Operator (WorkerConf 2018)

  • 1.
    Community-driven design: |> inTC39 Daniel Ehrenberg @littledan Igalia, in partnership with Bloomberg TC39 delegate WorkerConf 2018
  • 2.
  • 3.
    const cities =[ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, { name: "Valencia", population: 790000 } ];
  • 4.
    const cities =[ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, { name: "Valencia", population: 790000 } ]; cities.filter(city => city.population > 1000000); ===> [ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, ]
  • 5.
    const cities =[ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, { name: "Valencia", population: 790000 } ]; cities.filter(city => city.population > 1000000) .sort((a, b) => a.population - b.population); ===> [ { name: "Barcelona", population: 1609000 }, { name: "Madrid", population: 3166000 }, ]
  • 6.
    const cities =[ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, { name: "Valencia", population: 790000 } ]; cities.filter(city => city.population > 1000000) .sort((a, b) => a.population - b.population) .map(city => city.name); ===> ["Barcelona", "Madrid"]
  • 7.
  • 8.
    function sortByPop(arr) { returnarr.sort( (a, b) => a.population - b.population); } sortByPop(cities.filter( city => city.population > 1000000)) .map(city => city.name);
  • 9.
    Array.prototype.sortByPop = function() { return this.sort( (a, b) => a.population - b.population); } const cities = [ { name: "Madrid", population: 3166000 }, { name: "Barcelona", population: 1609000 }, { name: "Valencia", population: 790000 } ]; cities.filter(city => city.population > 1000000) .sortByPop() .map(city => city.name);
  • 10.
  • 11.
    cities.js // Highest populationcities Array.prototype.sortByPop = function () { return this.sort((a, b) => a.population - b.population); } export function bigCities(cities) { return cities.filter(city => city.population > 1000000) .sortByPop() .map(city => city.name); } combined.js import { bigCities } from "cities.js"; import { manyFans } from "music.js"; // CLASH music.js // Most popular musicians Array.prototype.sortByPop = function () { return this.sort((a, b) => a.popularity - b.popularity); } export function manyFans(musicians) { … }
  • 12.
    function sortByPop(arr) { returnarr.sort( (a, b) => a.population - b.population); } sortByPop(cities.filter( city => city.population > 1000000)) .map(city => city.name);
  • 13.
    function sortByPop() { returnthis.sort( (a, b) => a.population - b.population); } const cities = // ... cities.filter(city => city.population > 1000000) ::sortByPop() .map(city => city.name);
  • 14.
    function f(x) { //... } // Bind operator obj::f(x) ===> f.call(obj, x)
  • 16.
    Maybe soon! We arelooking into it Pipeline/bind is at Stage 1
  • 17.
    // ES6 DestructuringAssignment let x = 1, y = 2; [y, x] = [x, y]; ⇒ x === 2 y === 1
  • 18.
    Who is TC39? ●A committee of Ecma, with… ● JS developers ● Implementers ● Frameworks ● Academics ● etc
  • 21.
    Meetings ● Every twomonths ● For three days ● Summarize/review GitHub activity ● Move proposals through stages
  • 22.
    TC39 stages ● Stage1: An idea ● Stage 2: Agreed-on first draft ● Stage 3: Details worked out ● Stage 4: Battle-tested and standard
  • 23.
    History of :: ●Discussed :: in 2012 and earlier ● Developed by Dave Herman, Kevin Smith ● Implemented in Babel, 2015 by Ingvar Stepanyan ● Deferred until post-ES6
  • 25.
  • 26.
    library.js export function doubleSay(str){ return str + ", " + str; } export function capitalize(str) { return str[0].toUpperCase() + str.substring(1); } export function exclaim(str) { return str + "!"; } with-pipeline.js import { doubleSay, capitalize, exclaim } from "library.js"; let result = "hello" |> doubleSay |> capitalize |> exclaim; // ===> "Hello, hello!" ordinary.js import { doubleSay, capitalize, exclaim } from "library.js"; let result = exclaim(capitalize(doubleSay("hello"))); // ===> "Hello, hello!"
  • 27.
    History of |> ●Gilbert created |> repository, November 2015 ● Reached Stage 1, September 2017 ● Implemented in Babel, late 2017 by Henry Zhu, Justin Ridgewell ● Pursue two paths, March 2018
  • 28.
  • 29.
    Example function doubleSay (str){ return str + ", " + str; } function capitalize (str) { return str[0].toUpperCase() + str.substring(1); } function exclaim (str) { return str + '!'; } ...the following invocations are equivalent: let result = exclaim(capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = "hello" |> doubleSay |> capitalize |> exclaim; result //=> "Hello, hello!"
  • 30.
    Evolving to async... functiondoubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(capitalize( doubleSay("hello"))); result //=> [Object Promise]! let result = "hello" |> doubleSay |> capitalize |> exclaim; result //=> [Object Promise]!
  • 31.
    Oops, how canwe fix it? function doubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(await capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = ("hello" |> doubleSay |> capitalize) .then(exclaim); result //=> resolves to “Hello”
  • 32.
    Or, this wouldalso work function doubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(await capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = await ( "hello" |> doubleSay |> capitalize) |> exclaim; result //=> "Hello, hello!"
  • 33.
    A prettier solution(PR; proposed for C#) function doubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(await capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = "hello" |> doubleSay |> await capitalize |> exclaim; result //=> "Hello, hello!"
  • 34.
    However, this wouldbe different… function doubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(await capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = "hello" |> doubleSay |> (await capitalize) |> exclaim; result //=> [Object Promise]!
  • 35.
    Another nice solution functiondoubleSay (str) { return str + ", " + str; } async function capitalize (str) { let response = await capitalizeOnTheServer(str); return response.getCapitalized(); } function exclaim (str) { return str + '!'; } let result = exclaim(await capitalize( doubleSay("hello"))); result //=> "Hello, hello!" let result = "hello" |> doubleSay |> capitalize |> await |> exclaim; result //=> "Hello, hello!"
  • 36.
    Plan from here ●Pursue two options ("F#" and "smart pipeline") ● Write specifications and documentation ● Implement both in Babel, different flags ● Develop community consensus on F#, smart, and :: ● Propose a choice to TC39
  • 37.
    Steps needed forStage 2 ● First draft spec text ✔ ● Decide which variant … ● Agreement that we want the proposal …
  • 38.
    Steps needed forStage 3 ● Complete spec text … ● Signoff from reviewers, editor … ● Ready to pass off to native implementers …
  • 39.
    Steps needed forStage 4 ● Two implementations … ● Conformance tests … ● PR with editor signoff …
  • 40.
  • 41.
    Feedback on GitHubissues ● Does this work for you? ● Missing pieces? ● Handle a case differently?
  • 42.
    Pipeline GitHub feedback ●Proposal scope ● :: vs |> ● Await integration ● Avoiding currying ● Semantic details
  • 43.
    Test262 tests ● Testsshared between JS engines ● “If it’s not tested, it’s not compatible” ● Finds edge cases
  • 45.
    Pipeline test262 tests ●...None yet ● Could be useful for Babel development ● Wanna help?
  • 46.
    Documentation/Education ● Explain thefeature to contributors, learners ● Understand mental models for JS
  • 47.
    Pipeline documentation ● "Explainerdocuments" for each variant ● Wanted: Better introductory materials ● Wanted: Educators' perspective on the alternatives
  • 48.
    Implementation ● Open-source projectsaccepting contributions: ○ Babel ○ TypeScript ○ V8 ○ Acorn ● Features developed behind a flag/in a plugin ○ JSC ○ ChakraCore ○ SpiderMonkey ○ And many more
  • 49.
    Pipeline implementations ● Babeltransforms ● Want to write more?
  • 50.
    Attending TC39 asa delegate ● Join Ecma to come to TC39 meetings ● Contact me for more info, littledan@igalia.com
  • 51.
    ● Get involvedin TC39: https://tc39.github.io/ ● |> is at Stage 1 for compositional chaining ● Daniel Ehrenberg ● Twitter/GitHub @littledan ● littledan@igalia.com