Skip to content

Instantly share code, notes, and snippets.

@samuelgoto
Last active October 17, 2017 19:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelgoto/20f3015b8f29d1dfaff60e7c71591a47 to your computer and use it in GitHub Desktop.
Save samuelgoto/20f3015b8f29d1dfaff60e7c71591a47 to your computer and use it in GitHub Desktop.

THIS DRAFT HAS BEEN SUPERCEEDED BY THIS


This is a stream of random notes taken while talking to @rbuckton and @dherman about throw expressions and do expressions, as well as an early exploration of an attempt to get some clarity towards a cohesive/coherent story between the different statement types.

Categorization

This is not a perfect categorization, just one of many :) Also, originally categorized by @rbuckton in his presentation (and polished here talking f2f).

  • things that change the inner flow (continue, break)
  • things that return through a single pass (if, switch, try/catch)
  • things that return through many iterations (while, for)
  • things that abruptly terminate the scope (throw, return)
  • where to put yield?

Open Problems

Some tough problems currently with how do expressions are formulated:

  • what happens when you return/throw from these expressions (example)?
  • what happens when you continue/break from these expresions?
  • how do you return values? how do you return arrays?
  • can these things be async/awaited?
  • if you declare variables inside the expression do they go into the outer scope?

A design alternative

In this exploration (inspired by scala's for-expressions), here are some of the possibly different rules:

  • we disallow returning/throwning from these expressions (SyntaxError)
  • we disallow continuing/breaking from these expressions (SyntaxError)
  • we create a new keyword (e.g. yield, make, produce, use, generate, end, tail, etc) to enable things to be returned

In this specific exploration, we pick use as an example because it is short/meaningful, but other options should be considered.

This is a bit shady, but here is a strawman: use works like yield in that it can be called multiple times and the statement block can return multiple values. in if, try and switch it returns a single value (stops at first use of use?) and for for, while it returns an array (all use are called).

Examples

// let a = 2
let a = if (true) { use 1; } else { use 2; };

// let a = 2
let a = try { use throwsError(); } catch (e) { use 2; };

let a = switch (encoding) {
  case "utf8": use new UTF8Encoder()
  case "utf161e": use new UTF16Encoder()
  case "utf16be": use new UTF16Encoder()
  default: throw new Error("Unsupported encoding")
};

For iterators:

// let a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let a = for (let i = 0; i < 10; i++) { use i; };

// let a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let a = while (i < 10) { i++; use i; };

Syntax Errors

We deliberate throw a SyntaxError if use expressions call certain invalid statements to avoid ambiguities. For example:

TODO(goto): should "throws" be allowed? if so, why?

function b() {
  let a = while (true) {
    // SyntaxError is thrown because `return` isn't allowed inside a while-expression
    return 1
  }
}

for (let i = 0; i < 10; i++) {
  let b = for (let j = 0; j < 10; j++) {
    // SyntaxError is thrown here because `break` isn't allowed inside for-expressions.
    break;
  }
}

Corner cases

What happens in these cases?

// is a 'undefined'?
let a = if (b) {
  use c;
}

// Error thrown?
let a = try { throwsError(); };

// is a 'undefined'?
let a = while (false) { use c; }

// what does this return?
let a = for (let i = 0; i < 10; i++) {
  use for (let j = 0; j < 10; j++) {
    use j;
  }
}

Options for use semantics

Option 2) return-like semantics

let result = [];
let a = do { i++; result.append(i); if (i == 1) use result; } while (1);

let result = [];
let a = for (let i = 0; i < 10; i++) { result.append(i) if (i == 9) use result; };

Option 3) new syntax

let result = [];
let a = do { i++; result.append(i); } while (1) use result;

let result = [];
let a = for (let i = 0; i < 10; i++) { result.append(i) } use result;
@mathiasbynens
Copy link

The first example should say let a = 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment