samuelgoto / proposal-block-params

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

Can receivers change return values? #25

Open samuelgoto opened 6 years ago

samuelgoto commented 6 years ago

@ljharb's feedback:

Is it possible for receivers to change the value that gets returned? For example:

function a() {
  unless (false) {
    return 1;
  }
  return 2;
}

Is it possible, in any way, for unless to control what gets returned to a independently of what the block param returns lexically?

ljharb commented 6 years ago

(also @bmeck)

To clarify: is it possible in any way for unless to prevent return 2; from executing, short of invoking the block that contains return 1; lexically?

samuelgoto commented 6 years ago

It certainly feels awkward for unlesss to have the ability to return anything to a that wasn't written lexically inside of a in the block param.

Because receivers are written as functions in their own scope, there is nothing that they can do to change the return value of the functions. For example:

function unless(expr, block) {
  // I can store block, setTimeout on it, pass it around, etc. For example:
  // When executed, if block() returns, it throws a ContinuationException, 
  // something along the lines of this:
  // http://tronicek.blogspot.com/2008/08/nonlocal-transfer.html
  setTimeout(block);
  // returning 2 here doesn't return to the ```a``` function, but rather to the ```unlesss```
  // function.
  return 2;
}

It is interesting to note, though, that unless could use finally and throw an exception. For example:

function unless (expr, block) {
  try {
    block();
  } finally {
    throw 1;
  }
}

It is unclear to me what are the consequences of this and what are the possible solutions (e.g. swallowing exceptions? allowing?). Writing it down to make sure I don't forget what was discussed.

bmeck commented 6 years ago

We discussed an example of this at the meeting, with the assumption that the completion value slot is filled in a well defined way.

unless would write to a's completion value slot including with normal completions.

a would write to an implicit completion value and the above would roughly desugar to the following.

function a() {
  let a_completion = {
    type: 'normal',
    value: undefined
  };
  let block_completion = unless(false, () => {
    a_completion = {
      type: 'return',
      value: 1
    }
  });
  switch (a_completion.type) {
     case 'normal': break;
     case 'throw': throw a_completion.value;
     case 'return': return a_completion.value;
  }
}

Which would decouple non-exception values from changing the completion value of a. However, the question of if block_completion above would be able to overwrite a_completion for some types of completions. If so, which ones or why none.

I am not tied to any intent of the feature, but I feel like making some determination of pseudocode to be more concrete would make me able to more clearly discuss this.