tc39 / proposal-explicit-resource-management

ECMAScript Explicit Resource Management

Home Page:https://arai-a.github.io/ecma262-compare/?pr=3000

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

User-managed vs System-managed resources (over "explicit" vs "implicit")

chancancode opened this issue · comments

I know it's a bit late, and it may seem a bit pedantic/not very important, but I want pitch the possibility of renaming the feature. Sounds like a big change at this stage but I believe it's mostly non-normative.

Personally, I have always seen the "explicit resource management" as a bit of a placeholder name, that has perhaps outlasted its usefulness, and as we enter the phase where we have to teach this to developers, I think it is important to have a good name to talk about this and refer to the different parts .

I understand that "explicit" here is trying to contrast against "implicit" as in automatic/unobservable – i.e. garbage collection. And I think that is a fair way to describe the kind of patterns this feature is trying to make nicer, though even then arguably not very precise.

The problem is, as this feature has shaped up, it is very awkward to call this feature/proposal "explicit resources". To me, that sounds like a proposal that makes something "more explicit" – perhaps some kind of opt-out for GC that you then have to explicitly free() or something. It sounded like this is making some previously "implicit" (with perhaps a negative connotation, as in "unclear"/"ambigious"/"confusing"/"magic") patterns or steps and adding some ways to "make explicit" the resource management.

When in fact – this proposal/feature does the opposite, it is adding syntax and language features to make managing these kind of resources more automatic. It takes away some orchestration/boilerplate/non-standardized pattern (the disposal protocol/disposables interface) and remove the explicit steps required to do the clean up correctly. In other words, the clean up for these resources are now automatic, which sounds dangerously close to implicit. It is very awkward to call "an disposable object implementing the [Symbol.dispose] interface" an "explicit resource", when, the whole point of implementing that is to make that resource automatically cleaned up when used in conjunction with the using binding.

That is, in itself, very confusing to talk about IMO. But further adding to the confusion – there is now a need to talk about those legacy code/objects/patterns that was previously necessary before this feature was added. What do you call the kind of "resources" APIs like setTimeout(), which cannot be adapted to fit this proposal, because it does not return an object?

To me, a better way to make the distinction is that GC-released objects are "system-managed resources" and these types of disposables are "user-managed resources" (user as in user-land). I think that is an apt name, because even with this proposal, it does still place the burden on the user to manage the resource properly – ensuring that there is one owner (or else coordinate appropriately), that it is assigned to a using binding with the appropriate scope (or else manage calling the Symbol.dispose method some other way), and, if you do none of those things, the resource will still leak.

So – to be, explicit, what I am proposing/asking is that:

  • We officially rename this proposal to "User-managed resources proposal", in this repo and in other places for eventual archival purposes
  • In places where we refer to the two types of resources in spec text, normative or not, we consistently use the verbiage of "user-managed" and "system-managed" resources
  • Coordinate with documentation and outreach efforts (MDN, TypeScript, etc) to adopt these names

Very open to any other names but I would really like that we have a better and consistent way to describe this feature, both in the spec text, but also to better coordinate the community outreach/teaching/documentation effort going forward.

Some example phrasing:

  • JavaScript is a garbage-collected language, which is a form of automatic memory management. Unlike in languages like C/C++, you never have to allocate and free memory yourself, it's all handled for you by the system.
  • That being said, memory is only one type of resource we use in programming, and sometimes there is still a need for you, the programmer, to manage resource yourself. For example, when you open a file, it is your responsibility to eventually close – or dispose of – it, or else you will be leaking the resource. We call these type of resource user-managed resource.
  • The using keyword can be used to automate some of the boilerplate required to dispose a user-managed resource correctly.
  • The DisposableStack and AsyncDisposableStack can be used to coordinate then disposal of user-managed resource objects, particularly in contexts where the using keyword cannot be used.

If we want to quibble about the naming, I would add that, personally – I would steer away from "explicitly-managed", "manually-managed", etc, because it is strange to emphasize the "manual"/"explicit" part of the management in the context of discussing the using feature.

I agree that it would be confusing to refer to the using feature/syntax as facilitating "explicit" resource management.

Makes sense to me!

The explainer has a section on terminology and why we use the term "explicit".

Implicitly managed resources require no user interaction for cleanup by the consumer of the resource and are essentially "automatic", with the downside that the consumer of the resource has no control over object lifetime/memory/native handles/etc. Garbage collection, WeakMap, WeakSet, and WeakRef fall into this category, but so does FinalizationRegistry. FinalizationRegistry allows the producer of a resource to attach user-defined cleanup behavior, without the need for the consumer of the resource to perform cleanup. FinalizationRegistry would fall under the category of "user-managed", but still be implicitly handled by the runtime, and thus be automatic.

Explicitly managed resources give control of the resource's lifetime to the consumer of the resource, which allows for finer control and timely cleanup. I chose this name for the proposal as it adds two new capabilities to ECMAScript:

  • A well-defined API for imperative and explicit cleanup: Symbol.dispose/Symbol.asyncDispose/DisposableStack/AsyncDisposableStack
  • A syntax for declarative and explicit cleanup: using/await using

In my opinion, changing the name to something like "user-managed resources" only adds confusion due to the overlap with FinalizationRegistry. I also don't believe changing the name of the proposal at this late stage would be beneficial, as it would be likely to cause more confusion amongst folks who already know it as "Explicit Resource Management". While some proposals have undergone name changes at Stage 3 in the past, it's usually in response to a fundamental change to the proposal itself (features being split off, a specific change to the syntax/api that is referenced in the proposal name, etc.), and I would consider such a change to be a relatively high bar.

In fact, this proposal was originally known, simply, as "using statements" when it was first presented. However, the syntax underwent significant changes and back and forth, from using to try using, to using const, etc., so I gave the proposal the name "Explicit Resource Management" to instead describe the underlying capabilities and behavior this feature represents.

The name of the proposal aside, I see no reason why documentation about using/Symbol.dispose/DisposableStack/etc. needs to specifically mention "explicitly-managed" if that seems confusing. I might expect to see "disposable" referenced, since that is the term used in the specification text. I might also expect to see terms like "explicit lifetime" or "block-scoped lifetime" in reference to how using manages the lifetime of a resource based on the enclosing block scope.

That is, in itself, very confusing to talk about IMO. But further adding to the confusion – there is now a need to talk about those legacy code/objects/patterns that was previously necessary before this feature was added.

I think there is a need to talk about those legacy patterns. In some cases, existing APIs can be easily adapted to use [Symbol.dispose](). In other cases, you can use adopt and defer on DisposableStack to adapt legacy cleanup behavior.

What do you call the kind of "resources" APIs like setTimeout(), which cannot be adapted to fit this proposal, because it does not return an object?

setTimeout/clearTimeout and the like are a form of imperative explicit resource management, as the user still manages the lifetime of the resource (in this case, a handle), they just don't use the common API of Symbol.dispose and the resource cannot be used declaratively on its own. It's possible that WHATWG and/or NodeJS could introduce a createTimeout() function that returns a disposable, or a disposable Timeout class. In the worst case, you just use DisposableStack:

function createTimeout(ms, fn, ...args) {
  const stack = new DisposableStack();
  const handle = setTimeout(ms, fn, ...args);
  stack.defer(() => clearTimeout(handle));
  return stack;
}

async function f() {
  const controller = new AbortController();
  using abortTimeout = createTimeout(1000, () => controller.abort());
  ...
}

The explainer has a section on terminology and why we use the term "explicit".

Implicitly managed resources require no user interaction for cleanup by the consumer of the resource and are essentially "automatic", with the downside that the consumer of the resource has no control over object lifetime/memory/native handles/etc. Garbage collection, WeakMap, WeakSet, and WeakRef fall into this category, but so does FinalizationRegistry. FinalizationRegistry allows the producer of a resource to attach user-defined cleanup behavior, without the need for the consumer of the resource to perform cleanup. FinalizationRegistry would fall under the category of "user-managed", but still be implicitly handled by the runtime, and thus be automatic.

Explicitly managed resources give control of the resource's lifetime to the consumer of the resource, which allows for finer control and timely cleanup. I chose this name for the proposal as it adds two new capabilities to ECMAScript:

A well-defined API for imperative and explicit cleanup: Symbol.dispose/Symbol.asyncDispose/DisposableStack/AsyncDisposableStack
A syntax for declarative and explicit cleanup: using/await using

Yes, I understand and appreciate that those are the distinctions you are trying to carefully draw; I also acknowledge that it is late and naming things are hard. But I still think those are not the right name for those distinctions, especially after the features in the proposal landing (and I think it's a really great feature!).

In my opinion, changing the name to something like "user-managed resources" only adds confusion due to the overlap with FinalizationRegistry.

To me, that doesn't conflict – I chose/proposed "user-managed resources" because it only describe that the resource is managed in user space, it talks about who is expected to manage it (the user is required to do some action), and is neutral against how that management happens (what action is needed).

To me, the FinalizationRegistry is a system managed resource with customizable cleanup behavior; "user-defined cleanup behavior" doesn't conflict with the fact that the the resource lifetime is itself system-managed.

And ultimately – those are the kind of distinctions I am trying to make and why I think the current naming is confusing. I am hoping/looking for a name that focuses more on the "who" aspect, and rather than the "how" aspect. I think the name "implicit" vs "explicit" heavily implies the latter. For example, "declarative explicit cleanup" sounds like an oxymoron to me, and I'd argue that is the very common/default reaction.

It is possible to carefully define your way out of it, and I understand why to you, under your definitions, that is in fact not an oxymoron, but going forward it is going to be mostly casual reading (developers spending a few minutes browsing the MDN docs, or coming up across/describing the feature in 280 characters). And I think the current terminology choices are unnecessary confusing for that purpose.

Again, not attached to the particular naming choices I proposed and if we can agree there is a need here, I would be more than happy to enumerate the criteria and brainstorm other options that checks all the boxes.

I also don't believe changing the name of the proposal at this late stage would be beneficial, as it would be likely to cause more confusion amongst folks who already know it as "Explicit Resource Management". While some proposals have undergone name changes at Stage 3 in the past, it's usually in response to a fundamental change to the proposal itself (features being split off, a specific change to the syntax/api that is referenced in the proposal name, etc.), and I would consider such a change to be a relatively high bar.

Again I understand that it is late, but I think it is still important to consider it. We are balancing the folks who would be confused by the name change while this proposal is still active, against all the futures developers who would come across the name (on MDN and such) and be confused by it.

I would argue the former is a short term thing, and the impact is actually limited, because as the proposal nears the implantation stage, the need to refer to the proposal itself (rather than the finalized feature) will naturally dwindle.

The name of the proposal aside, I see no reason why documentation about using/Symbol.dispose/DisposableStack/etc. needs to specifically mention "explicitly-managed" if that seems confusing. I might expect to see "disposable" referenced, since that is the term used in the specification text. I might also expect to see terms like "explicit lifetime" or "block-scoped lifetime" in reference to how using manages the lifetime of a resource based on the enclosing block scope.

Yes, why I think changing the name of the proposal is itself beneficial and, on balance, worth it and reduces overall confusion, we could (in addition or instead) also focus on preventing the terms "explicit vs implicit" from entering the developers lexicon, which is ultimately what I cares about.

For example, we could agree on what to call various things, put together a terminology section with the agree upon names, make sure the proposal and spec text is internally consistent with these names and provide some guidance on how to teach/talk about the feature and the underlying concepts.

In that world, I would expect that there is a note somewhere along the lines of – this type of resource is previously known as "explicitly managed resource, hence the proposal name, but X is now the preferred terminology". I think if the proposal is technically called "explicit resource management", and you would find the babel plugin under that name, but otherwise the word "explicit resources" or "explicit management" never comes up, that is probably fine. Personally I still think you may as well rename the proposal, but it's a world that I can easily live with.

I think there is a need to talk about those legacy patterns. In some cases, existing APIs can be easily adapted to use Symbol.dispose. In other cases, you can use adopt and defer on DisposableStack to adapt legacy cleanup behavior.

Yes I understand that and I share the expectation that overtime things will adapt to this protocol, allowing them to be implicitly/automatically... declaratively managed with the using keyword. I am just pointing out that, in the meantime, there is a terminology table/Venn diagram to fill, with aspects like "whether it participates in the [Symbol.dispose] protocol", "whether it was consumed with the using keyword", etc. And I think the intuitive connotation of "implicit" vs "explicit" is strong and takes up/conflicts with a bunch of space in that table.

I mentioned this in another thread as well, but I will repeat it here. The choice for "implicit" vs "explicit" is deeply rooted in memory management nomenclature. With respect to memory management, "implicit" (or "automatic", "dynamic") refers to garbage collection, while "explicit" refers to explicit memory management 123, where memory is managed by the user.

Resource management is highly analogous to memory management, thus a similar distinction applies: "implicit" (or "automatic", "dynamic") resource management occurs when resource lifetime is managed by the GC (and thus via objects like WeakRef and FinalizationRegistry), while "explicit" resource management occurs when resource lifetime is managed by the user (via imperative calls or declarative syntax, e.g., using).

These parallels are born from the strongly connective tissue that binds these two concepts of memory and resources, and I'd prefer we keep them aligned. "implicit" (or "automatic", "dynamic") vs "explicit" is already in the developer's lexicon in languages outside of JS, and I think changing the name would only muddy the waters here by repurposing or introducing new terms that aren't nearly as closely related.

Footnotes

  1. https://people.cs.umass.edu/~emery/pubs/04-17.pdf

  2. https://dl.acm.org/doi/abs/10.1145/1094811.1094836

  3. https://theory.stanford.edu/~aiken/publications/theses/gay.pdf

@chancancode: I don't know if I have swayed your opinion on this, but I'd like to close out this issue if there is to be no further discussion on the topic.

Yeah well, I think given the status quo, I needed to sway your opinion on it, and it doesn’t seem like I have succeeded :) But either way, yea, if there isn’t going to be movement on this then closing seems appropriate