5 ms·
Exactly this. No idea why you were downvoted. The problem they are trying to solve is that the programmer could forget to wrap an object creation with try. But
by akoboldfrying 1y ago
Exactly this. No idea why you were downvoted.
The problem they are trying to solve is that the programmer could forget to wrap an object creation with try. But their solution is just kicking the can down the road, because now the programmer could forget to write "using"!
I was thinking that a much better solution would be to simply add a no-op default implementation of dispose(), and call it whenever any object hits end-of-scope with refcount=1, and drop the "using" keyword entirely, since that way programmers couldn't forget to write "using". But then I remembered that JavaScript doesn't have refcounts, and we can't assume that function calls to which the object has been passed have not kept references to it, expecting it to still exist in its undisposed state later.
OTOH, if there really is no "nice" solution to detecting this kind of "escape", it means that, under the new system, writing "using" must be dangerous -- it can lead to dispose() being called when some function call stored a reference to the object somewhere, expecting it to still exist in its undisposed state later.
- mistercow 1y agoAnother point there is that JS has always gone to great lengths not to expose the GC in any way. For example, you can’t enumerate a WeakSet, because that would cause behavior to be GC dependent. Calling dispose when an object is collected would very explicitly cause the GC to have semantic effects, and I think that goes strongly against the JS philosophy.
- masklinn 1y agoFinalizationRegistry was added, like, 5 years ago.
- rafram 1y agoFinalizers aren’t destructors. The finalizer doesn’t get access to the object being GC’d, for one. But even more crucially, the spec allows the engine to call your finalizer anywhere between long after the object has been GC’d, and never. They’re basically a nice convenience for noncritical resource cleanup. You can’t rely on them.
- masklinn 1y agoYes? Congratulation you know what a finalizer is? I was replying to this: > would very explicitly cause the GC to have semantic effects, and I think that goes strongly against the JS philosophy. Do you disagree that a finalizer provides for exactly that and thus can not be "strongly against the JS philosophy"?
- mistercow 1y agoI mean it’s an explicit violation of that philosophy as noted in the proposal: > For this reason, the W3C TAG Design Principles recommend against creating APIs that expose garbage collection. It's best if WeakRef objects and FinalizationRegistry objects are used as a way to avoid excess memory usage, or as a backstop against certain bugs, rather than as a normal way to clean up external resources or observe what's allocated.
- pwdisswordfishz 1y agoYes, it and WeakRef are exceptions, but they are the only ones, designed to be deniable – if you delete globalThis.WeakRef; and delete globalThis.FinalizationRegistry; you go back to not exposing GC at all. WeakRef even has a special exception in the spec in that the .constructor property is optional, specifically so that handing a weak reference to some code does not necessarily enable it to create more weak references, so you can be also limited as to which objects' GC you can observe. Though another problem is that the spec does not clearly specify when an object may be collected or allow the programmer to control GC in any way, which means relying on FinalizationRegistry may lead to leaks/failure to finalize unused resources (bad, but sometimes tolerable) or worse, use-after-free bugs (outright fatal) – see e.g. https://github.com/tc39/ecma262/issues/2650 https://github.com/tc39/ecma262/issues/2650
- mistercow 1y agoFair, I wasn’t aware of that. But even so, there’s a big difference between a wonky feature intended for niche cases and documented almost entirely in terms of caveats, and “this is the new way to dispose of resources”. And the point that this kind of thing is against the JS philosophy is pretty explicit: https://w3ctag.github.io/design-principles/#js-gc https://w3ctag.github.io/design-principles/#js-gc
- morsecodist 1y agoI feel it doesn't make sense to conflate resource management with garbage collection. The cleanup actions here are more like releasing a lock, deleting temporary files, or closing a connection. This doesn't lead to a lack of safety. These resources already need to deal with these uninitialised states. For example, consider a lock management object. You shouldn't assume you have the lock just because you have a reference to the manager resource. It's totally normal to have objects that require some sort of initialization.
- akoboldfrying 1y agoYou seem to be making the argument that the new "using" doesn't create any new, dangerous behaviours. But I already agree with that -- I'm saying that it doesn't make anything better than it was before (a programmer who forgets to write "try" could equally forget to write "using", so the required level of programmer discipline is unchanged), plus, if you mistakenly persuade yourself that it's safe to write "using" everywhere all the time, you can introduce bugs like locks that are released too early. Why might a programmer persuade themselves of that? Because otherwise "using" has no benefit at all, beyond a slightly sweeter syntax for wrapping the function body in "try ... catch (x) { for (o of objs) o.dispose(); }”. > I feel it doesn't make sense to conflate resource management with garbage collection Memory is just a resource, one that conveniently doesn't require anything to be done urgently when that its lifetime ends (unlike, say, locks). GC is a system for managing that resource -- or any resource with the same non-urgency property. For example, you can imagine a GC-based resource management system for a pool of DB connections. > You shouldn't assume you have the lock just because you have a reference to the manager resource. Some languages make it necessary to code in this way, where you need to always check if something is in a valid state before doing something with it, but that's unfortunate, because there are languages (like C++, which is horrible in so many other ways) where you can maintain the invariant that, if you have a reference to a lock, the associated resource is locked. The general idea -- Make Invalid States Unrepresentable -- is a fantastic way to improve code quality, so we should always be looking for ways to incorporate it into languages that don't yet have it. Parse Don't Validate is the same underlying idea.