tc39/proposal-function.sent

May yield* send the last yield value?

Opened this issue · 7 comments

hax commented
YieldExpression:yield*AssignmentExpression
...
4. Let received be NormalCompletion(undefined).

May it update to 4. Let received be NormalCompletion(LastYieldValue) so we can satisfy:

function *g1() {
  yield function.sent
}
function *g2() {
  yield* g1()
}

g2().next(42) // => {value:42}

But allow delegate generator access the last yield value also have dark side:

function* service() {
  const token = function.sent
  if (auth(token)) {
    yield* serviceOf3rdParty()
  }
}

const serv = service()
serv.next('my-secret-token')

I'd rather not see that happen by default given the second case, perhaps a variant of yield* or a function that forwards a value e.g.:

function* g2() {
    yield** g1()
}

// Or just a function (which can even be written in userland)

function* g2() {
    yield* withInitial(function.sent, g1())
}
hax commented

Consider the common use cases (if i can deal with the value of function.sent, process it in current generator, or delegate to sub generator), I think we need a ergonomic way to pass the value to sub generators. Besides new syntax like yield **g(), another possibility is changing function.sent to a function so we can have rich semantic :

function *g2() {
  // function.sent() seems not a proper name, so let's use function.readValue()
  const x = function.readValue() 
  if (i_can_deal_with_it()) {
    ...
  } else { // delegate to sub generator
    yield *g1() // g1 can read x via function.readValue()
  }
}

For the case of previous dark side example,

function* service() {
  // function.readValue(true) means the value is consumed
  // calling function.readValue() again before next next(v) would throw
  const token = function.readValue(true) 
  if (auth(token)) {
    yield* serviceOf3rdParty() // so serviceOf3rdParty can't see token
  }
}

const serv = service()
serv.next('my-secret-token')

It makes no sense to be to have it be "consumeable". It's a single static value, like a const; you can access it as many times as you need. yield * delegates control to an iterator, not a generator, so i don't think it's sensible or possible to pass it along.

hax commented

It makes no sense to be to have it be "consumeable".

I'm not sure whether "consumable" is good or bad, but it's really consuming values from next(value) in function.sent usage.

It's a single static value, like a const;

It's not like const, every time after yield the value would change.

you can access it as many times as you need.

This is just current semantic, but I think it's not a hard requirement. As I understand, the only core requirement uptonow is providing a way to get the first value sent by next().

yield * delegates control to an iterator, not a generator, so i don't think it's sensible or possible to pass it along.

This issue is really about that. yield * g() will also delegate next(value) except the first call. Actually other proposals like iterator helpers also keep delegate next(value). So it 's of coz "sensible" or "possible" to pass it along, for example via userland code like yield* withInitial(function.sent, g1()) though it's not very ergonomic.

hax commented

The essential problem of this issue is, currently yield* g will always start g with g.next(undefined), changing that to g.next(function.sent) (if keep the syntax untouched) is theoretically a breaking change, for example, code like below will break.

function iter() {
  return {
    [Symbol.iterator]() { return this },
    next(v) {
      // assume client code never send undefined after first call
      if (v === undefined) { // first call, 
        // do some stuff for setup
      } else {
        // process
      }
    }
  }
}

It's not like const, every time after yield the value would change.

you're right, my mistake - it's more like a live binding, as if from an import.

theoretically a breaking change

That's true, but if you design an iterator like that, anyone could break it very easily (including, with yield undefined) so i don't think that's a reasonable thing for us to be concerned about.

hax commented

so i don't think that's a reasonable thing for us to be concerned about.

I'm not sure about that. If a big site had such code (of coz I hope it would never happen), land such change would "break the web" anyway. 😅