samuelgoto / proposal-block-params

A syntactical simplification in JS to enable DSLs
205 stars 8 forks source link

completion value #10

Open samuelgoto opened 7 years ago

samuelgoto commented 7 years ago

WRT this, would the current semantics of the completion values of blocks be sufficient? Examples where it is not?

littledan commented 7 years ago

We've discussed completion values before, e.g., in the context of do expressions. It was brought up as a possible disadvantage. Specifically, completion values are kinda weird; it's currently just expose as a big set of unlikely cases for eval. I bet the most common time this comes up for eval is evaluating an expression, and that people might not know all the rules for completion values for statements. Should we really promote them into new language constructs going forward like this? An alternative would be to use return within these blocks.

samuelgoto commented 7 years ago

people might not know all the rules for completion values for statements.

That's a good question and I agree that it is awkward ...

An alternative would be to use return within these blocks.

In the current formulation, I think it is desirable to make returns lexically scoped, non local. For example, you'd want:

function getid() {
  unless (document.cookie) {
    // you'd want this return to return lexically, rather than to the block param.
    return null;
  }
  // ...
}

Certainly not trivial, from a spec perspective and an implementation perspective (from @adamk), but I think I found general convergence chatting informally with people that that's "the right thing to do" (@waldemarhorwart).

On the other hand, it is also desirable for the block param to return values. Specifically, we could turn a lot of the statements into expressions if the block params could return values. For example:

let filtered = foreach({item, index} in [1, 2, 3, 10000, 10000000]) {
  if (item < 10) {
    item
  }
}
// filtered = [1, 2, 3]

One alternative that I was exploring is possibly to introduce a new keyword to represent the local return. For example:

let filtered = foreach({item, index} in [1, 2, 3, 10000, 10000000]) {
  if (item < 10) {
    use item;
  }
}
// filtered = [1, 2, 3]

I'd love to hear other alternatives or observations on requirements.

littledan commented 7 years ago

Well, for me, this is the thorniest part of the proposal. I wish we could just use return, but I understand your point that users might not conceive of these blocks as their own functions.

I'm not sure if this is the right place to be suddenly making the last statement in a block have a usable value; people who like this construct might find it a surprising inconsistency that putting an expression at the end of a normal function doesn't return it, that if can't be used as an expression except in this trailing case, etc.

I share @ajklein's implementation concern about "lexically scoped return". I believe this was considered for arrow functions to have "lexical" break or continue in the ES6 cycle, and those similar implementation concerns should be relevant here. It would be somewhat more complicated than iterator return, since any call could invoke this nonlocal return to leave, and you'd have to hit the iterator return and finally blocks along the way.

samuelgoto commented 7 years ago

I'm not sure if this is the right place to be suddenly making the last statement in a block have a usable value;

Do you find that a new keyword, like use, would mitigate some of that? Any other ideas/suggestions?

let filtered = foreach({item, index} in [1, 2, 3, 10000, 10000000]) {
  if (item < 10) {
    use item;
  }
}
// filtered = [1, 2, 3]
littledan commented 7 years ago

Not sure if this is the most helpful, but really, I feel uncertain about any sort of new syntax for sending the value out. It just seems like more cognitive load to have a new way of passing out values in a construct that otherwise doesn't stand out syntactically, where that mechanism isn't available elsewhere. I'd prefer either using an expression for the whole body, or using return.

samuelgoto commented 7 years ago

I'd prefer either using an expression for the whole body

Hum interesting, kinda of like we did for arrow functions? What form would that take? Something like the following?

let filtered = foreach({item} in [1, 2, 3]) {
  item > 10 ? item : undefined
}

I'm assuming the following would create all sorts of ambiguity?

let filtered = foreach({item} in [1, 2, 3])
  item > 10 ? item : undefined

I think return is a non-starter if we want to enable something like unless.

littledan commented 7 years ago

Yeah, I guess omitting the brackets is a bit of a non starter. I don't have a good solution here.