Eager return !

Hello there !

Please let me introduce myself quickly. I'm a junior software engineer with few years of JavaScript experience now with real world projects (l should mention those were little projects, no hard optimizations).

Now the idea of this proposal of course is to discuss it and debunk the idea rather than to impress.

Proposal

Context

It is common to call a function to do a task and return results as soon as possible. Sometimes we call a function that calls other functions to look for that same results. In this particular situation, we need to return results to "some" top calling code as soon as possible.

Example

function return42 () {
  return 42
}

and

// Calling function
function caller() {
  if (return42() === 42) return return42()
  else {
    // continue
  }
}

Comments

By definition, every function would like to be deterministic and to have full control over its code and memory (of course we might need side effects through global variables or IO streams).
This is to say it is uncommon (often uneeded) to let the callee function take control over its caller. I think it is unecessary and time wasting to find situations where we need that because of course we could find such situations but it is very uncommon in the mindset of computing algorithms.

Nevertheless, I think the example we raised earlier is very common, and well defined.

(I think you already see where I'm heading to!)


Proposale

In the context described above, I would like to see a new kind of return defined and added to JavaScript.

I think defining a return that goes through the stack to the top most calling code is unfeasible and/or too confusing. We should think of an agreement between the caller and the callee function so that the developer could decide clearly then declare the particular situation described earlier between a caller and a callee function.

(We already had async function_n with await function_n() added to JavaScript without any problems but with new solutions !)

My propositions is to add two keywords similar as async and await to describe this situation. Something like give and found (I'm not English native, maybe not the best words).

Example

function `give` (`like 42`) return42 () {
  `found` 42
}

and

// Calling function
function `give` (`like 42`) caller() {
  `found` return42()
  // otherwise just continue !! So no if-else block here ! 
  // continue
}

Second example

(I can't think now of a structure to define like)
But I can think of something similar to:

like obj: [obj.key > 40] for instance.

Please let me know what do you think !! I will definitely get back to this thread the day after tomorrow as tomorrow I'm passing a test for a job (fingers crossed x)).

Thank you !

As I copied this, which was edited few days ago, I'm now thinking also of try-catch / throw which is similar to what I ought to in regard of the flow of execution !!!! This time not throwing an Error, but giving a return value !

Sounds like algebraic effects?

Thank you @senocular for the link, I skimmed that and think it is quite similar regarding the flow of execution.
I think the aim is different though.
But let me read that again when I have time maybe tomorrow,
(I was passing a coding-game for a job and jeez those tests are so frustrating, I lose it all when I see that clock. I'm refusing to complete it haha)

I think you're right about this idea being fairly similar to try/catch/throw. In fact, I don't think it would be too much effort to implement something like this in userland.

Instead of this:

function `give` caller(x, y) { ... }

write this:

function caller(give, x, y) { ... }

And instead of this:

`found` return42();

write this:

give(return42());

Then use a utility receive() function to capture the eager-return value, and voila! With a small user-land library, you have this feature.

Full example:

// The utility library

class LongReturn extends Error {
  constructor(value) {
    super('This got uncaught! That is not supposed to happen.');
    this.value = value;
  }
}

function give(value) {
  throw new LongReturn(value);
}

export function receive(callback) {
  try {
    return callback(give);
  } catch (err) {
    if (err instanceof LongReturn) return err.value;
    throw err;
  }
}

// ...elsewhere...

function fn1(give, randomBool) {
  return fn2(give, randomBool)
}

function fn2(give, randomBool) {
  if (randomBool) {
    return 'slow path';
  } else {
    give('long path');
  }
}

const randomBool = Math.random() > 0.5;
const result = receive(give => fn1(give, randomBool));

console.log(result);
2 Likes

Thanks @theScottyJam for the follow,
Indeed, I really like the example, It is making the idea clearer haha

I thought of using try,catch/throw to return results, but isn't it so counter-intuitive ??? I never saw that nowhere.

Edit: my bad, you clearly said this is meant as a library, hum very interesting.
Really impressed by your quick example !! ty

btw, you can (or should) avoid the else there

@caub always should for readability, true. But I was illustrating the case so making it obvious.
How do you like the idea of eager return?
Thanks

Isn't the pattern-matching proposal addressing what you described?

I'm not aware of that but I hardly believe.. I can't imagine a syntax pattern here

It really involves a new flow of execution, if-else, switch, loops,. .. all don't fulfill this. Jumping in the stack of functions calls is not something possible in high level languages, but still I think I brought here a nice usecase where it is useful.
I know try-catch flow could be used and @theScottyJam neatly suggested an simple implementation of that. Very useful as a library but uncommon and misleading in regard of the common computing mind.