Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should element finishers run interspersed with static field initializers? #42

Closed
littledan opened this issue Jan 6, 2018 · 7 comments
Closed
Labels

Comments

@littledan
Copy link
Member

Example of why this would be expected to occur: If a finisher were to be used to implement a static field (e.g., a static private field), it may want to evaluate its initializer thunk. If this happens, you'd expect all of the static field initializers to be run in top-to-bottom order.

Currently, though, all finishers run after all class field initializers. This doesn't come from @wycats and @bterlson 's decorators spec; it is just an artifact of how I combined decorators with fields. Should they be interspersed? For example, the logic could be,

For each class element,
  Run its initializer, if it exists
  Run the class element's finishers (finishers added by inner decorators before outer ones)
Run the class finishers
@wycats
Copy link
Collaborator

wycats commented Jan 7, 2018

@littledan if I'm remembering what I proposed correctly in the Munich Resolution on class evaluation order, static fields were intended to be run top-to-bottom before finishers (step 10 in that document before step 11).

Why does it matter whether an element's finisher runs immediately after its initializer runs or after all initializers run?

@littledan
Copy link
Member Author

Does "apply decorator transformations" refer to finishers? It doesn't seem to call out element finishers vs class-level finishers. Anyway if there are good reasons for this order I'm fine with sticking with them. Here's why I'm asking about a change.

Let's say we do something like tc39/proposal-static-class-features#8 and there are many decorators that implement static private fields in terms of static private accessors. A particular decorator usage might look like this, with the definition below:

let order = []
class Foo {
  static a = order.push(1);
  @staticConstField static #b = order.push(2);
  static c = order.push(3);
}

function staticConstField(descriptor) {
  assert(descriptor.hasOwnProperty('initializer'));
  let value;
  return {
    key: descriptor.key,
    descriptor: {
      get() { return value; }
    },
    finisher(klass) { value = descriptor.initializer.call(klass); return klass }
  };
}

Seeing this sort of code, I'd really expect order to be [1, 2, 3]. However, if we don't intersperse element finishers, it'd be [1, 3, 2]. (If we don't run the initializer in a finisher, there's no way we could set this properly, and the order would be [2, 1, 3].)

@wycats
Copy link
Collaborator

wycats commented Jan 8, 2018

@littledan you're right, I got my own proposal wrong, and you're right about the reason.

I think what you're suggesting is that element finishers would run either interspersed with decorator calls in Step 6, or at least before Step 7.

I agree completely that it should be possible to write a decorator that takes what looks like a field (either public or private, static or instance) with an initializer, and that it should be possible for such a decorator to change the field into an accessor and retain the original evaluation order. (that's what your saying, right?)

Is there some reason that interspersing the decorators with the finishers in step 6 works better than running the finishers after the decorators after step 6 but before step 7?

@littledan
Copy link
Member Author

I was actually picturing something a little bit different. Static field initializers have to run after the class TDZ ends, which means after class decorators. So, I thought element finishers would be interspersed with calls in Step 10, with class finishers running as Step 11 (assuming that's what Step 11 meant; I assume the class decorators are taken into account as part of Step 8).

@wycats
Copy link
Collaborator

wycats commented Jan 17, 2018

@littledan I think I'm fine with those semantics.

Can you write a little array.push example that illustrates the semantics you have in mind in terms of edge cases?

(aside: I'm really enjoying that example style as a quick way to get my head around what is being proposed)

@littledan
Copy link
Member Author

Ugh, now that I think about this a bit more, this might be a dead end. To intersperse finishers and other things, the only way I could see doing this is by actually putting them on the elements list and sending them to the class decorators. This seems like a whole bunch of overkill--I don't think finishers need to be visible like that. Maybe it's enough to just have them run at the end.

@littledan
Copy link
Member Author

Closing per #42 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants