thefrontside / effection

Structured concurrency and effects for JavaScript

Home Page:https://frontside.com/effection

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Confusion in docs on setting function name as label

getify opened this issue · comments

In this section of the API docs, the following is said:

Function names
A final trick is that if our operation is a generator function, then we can name the function to automatically set the name label:

import { fetch, withLabels } from 'effection';

export function fetchWeekDay(timezone) {
  return function* fetchWeekDay() {
    let response = yield fetch(`http://worldclockapi.com/api/json/${timezone}/now`);
    let time = yield response.json();
    return time.dayOfTheWeek;
  }
}

If I was understanding correctly, I would have expected this instead:

import { fetch, withLabels } from 'effection';

export function fetchWeekDay(timezone) {
  return function* fetchWeekDay() {
    let response = yield fetch(`http://worldclockapi.com/api/json/${timezone}/now`);
    let time = yield response.json();
    return time.dayOfTheWeek;
  }();          // <---- IIGFE, invoked
}

IOW, I was expecting the exported "normal function" to actually run the inner generator (to actually return the iterator), so that calling the outer "normal function" was effectively the same as if you had just called the original inner generator function (but that you got the name label set).

It's confusing to me why you would just have the outer function return the generator as opposed to running it (and thus returning the iterator). Is this a mistake in the docs?

If not and that's the expected pattern, I might suggest clarifying in that portion of the docs that it's intentional, and why you are returning the generator from a function instead of calling it. For example, you could say, "Effection will call the exported function, and if it returns a generator instead of an iterator, it'll automatically call the returned generator to get the iterator", or something like that.

Ah, I see, this may not be entirely clear. Thanks for reporting!

We support both Generator and GeneratorFunction as an operation. When you have a generator function like this:

function* myGeneratorFunction() {
  yield sleep(2000);
  console.log("hello world");
}

Both of these are valid ways of running them with Effection:

run(myGeneratorFunction); // passing in the function itself
run(myGeneratorFunction()); // passing in the actual generator!

We experimented a bit back and forth with this, and eventually found that allowing both is the easiest and most flexible.

Unfortunately, in the second case, where we pass in the generator itself, there is no way to obtain the name of the generator function (in this case: myGeneratorFunction), from the generator itself. JavaScript does not provide such an API. Essentially you cannot get a handle to the generator function that created the generator. Therefore, unfortunately, we can't infer the name of the generator function. This section of the docs is trying to clarify this subtle distinction, and why sometimes, we might not be able to infer the name. But it could do a better job of explaining why this is the case.

As a further refinement. Any function that returns an operation is itself an operation (not just a generator function). Async functions are also operations. The reason for this is that it is the mechanism to capture a refererence to the currently running task as it is passed in as the argument to the function.

For example, task => task => task => function*(task) {} is also a valid operation.