Hacker Newsnew | past | comments | ask | show | jobs | submit | mst's commentslogin

Oh my word.

    const defer = f => ({ [Symbol.dispose]: f })

    using defer(() => cleanup())
That only just occurred to me. To everybody else who finds it completely obvious, "well done" but it seemed worthy of mention nonetheless.


Note that depending on use case, it may be preferrable to use `DisposableStack` and `AsyncDisposableStack` which are part of the `using` proposal and have built-in support for callback registration.

This is notably necessary for scope-bridging and conditional registration as `using` is block-scoped so

    if (condition) {
        using x = { [Symbol.dispose]: cleanup }
    } // cleanup is called here
But because `using` is a variant of `const` which requires an initialisation value which it registers immediately this will fail:

    using x; // SyntaxError: using missing initialiser
    if (condition) {
        x = { [Symbol.dispose]: cleanup };
    }
and so will this:

    using x = { [Symbol.dispose]() {} };
    if (condition) {
        // TypeError: assignment to using variable
        x = { [Symbol.dispose]: cleanup }
    }
Instead, you'd write:

    using x = new DisposableStack;
    if (condition) {
        x.defer(cleanup)
    }
Similarly if you want to acquire a resource in a block (conditionally or not) but want the cleanup to happen at the function level, you'd create a stack at the function toplevel then add your disposables or callbacks to it as you go.


What is the purpose of DisposableStack.move()? Can it be used to transfer the collected .defer() callbacks entirely out of the current scope, e.g. up the call stack? Probably would be easier to pass DisposableStack as an argument to stack all .defer() callbacks in the caller's context?


Yup, or to transfer them anywhere else. One use case is for classes which allocate resources in their constructor:

    class Connector {
      constructor() {
        using stack = new DisposableStack;
        
        // Foo and Bar are both disposable
        this.foo = stack.use(new Foo());
        this.bar = stack.use(new Bar());
        this.stack = stack.move();
      }
      
      [Symbol.dispose]() {
        this.stack.dispose();
      }
    }
In this example you want to ensure that if the constructor errors partway through then any resources already allocated get cleaned up, but if it completes successfully then resources should only get cleaned up once the instance itself gets cleaned up.


> Probably would be easier to pass DisposableStack as an argument to stack all .defer() callbacks in the caller's context?

The problem in that case if if the current function can acquire disposables then error:

    function thing(stack) {
        const f = stack.use(new File(...));
        const g = stack.use(new File(...));
        if (something) {
            throw new Error
        }
        // do more stuff
        return someObject(f, g);
    }
rather than be released on exit, the files will only be released when the parent decides to dispose of its stack.

So what you do instead is use a local stack, and before returning successful control you `move` the disposables from the local stack to the parents', which avoids temporal holes:

    function thing(stack) {
        const local = new DisposableStack;
        const f = local.use(new File(...));
        const g = local.use(new File(...));
        if (something) {
            throw new Error
        }
        // do more stuff

        stack.use(local.move());
        return someObject(f, g);
    }
Although in that case you would probably `move` the stack into `someObject` itself as it takes ownership of the disposables, and have the caller `using` that:

    function thing() {
        const local = new DisposableStack;
        const f = local.use(new File(...));
        const g = local.use(new File(...));
        if (something) {
            throw new Error
        }
        // do more stuff

        return someObject(local.move(), f, g);
    }
In essence, `DisposableStack#move` is a way to emulate RAII's lifetime-based resource management, or the error-only defers some languages have.


(const local should be using local in the snippets).


I wrote about this last year, it's one of my favourite bits of this spec: https://jonathan-frere.com/posts/disposables-in-javascript/#...

TL;DR: the problem if you just pass the DisposableStack that you're working with is that it's either a `using` variable (in which case it will be disposed automatically when your function finishes, even if you've not actually finished with the stack), or it isn't (in which case if an error gets thrown while setting up the stack, the resources won't be disposed of properly).

`.move()` allows you to create a DisposableStack that's a kind of sacrificial lamb: if something goes wrong, it'll dispose of all of its contents automatically, but if nothing goes wrong, you can empty it and pass the contents somewhere else as a safe operation, and then let it get disposed whenever.


Just like golang. Nice.


I regularly add Symbol based features to JS libraries I'm using (named methods are riskier, of course)

    import { SomeStreamClass as SomeStreamClass_ } from "some/library"
    export class SomeStreamClass extends SomeStreamClass_ {
      [someSymbol] (...) { ... }
      ...
    }
I have not blown my foot off yet with this approach but, uh, no warranty, express or implied.

It's been working excellently for me so far though.


Much nicer than just adding your symbol method to the original class. :p


13 days late but for posterity:

Yes. Not Wanting To Do That was the motivating factor for coming up with this approach :D


I guess it could be improved with a simple check if SomeStreamClass_ already has someSymbol and then raise an exception, log a warning or some such.


8 days late but for posterity:

So far I've only ever been using a private symbol that only exists within the codebase in question (and is then exported to other parts of said codebase as required).

If I ever decide to generalise the approach a bit, I'll hopefully remember to do precisely what you describe.

Possibly with the addition of providing an "I am overriding this deliberately" flag that blows up if it doesn't already have said symbol.

But for the moment, the maximally dumbass approach in my original post is DTRT for me so far.


If you want to play with this, Bun 1.0.23+ seems to already have support: https://github.com/oven-sh/bun/discussions/4325


I feel like it would be creepy if the kid was using it without anybody ever checking up on it ... but I think all of my friends with kids would say that the answer to that is "parenting."

I mean, giving a kid an unlocked iPad and not bothering to do basic supervision can also have really creepy results, so I'm unconvinced that something like your work actually makes anything worse in the negligent parenting situation, and seems like it could be a lot of fun in the competent parenting one.

If you haven't already done this, I'd note that I can think of a number of parents who would probably rather enjoy a version of story mode that let them collaborate with their child and your code to put together a bedtime story before they turn it off for the night and tuck the kid into bed.


OpenAI stuff in general seems (to me, at least) to be overly positive and confident in terms of how it replies.

While I make no foolish claims that it's perfect, I've found Claude feels much less arrogant, and was genuinely appreciative when one of its replies started with an (accurate, of course I checked primary sources to verify that) analysis of the first half of my question, and then for the more obscure second half said "I'm not sure if I can answer that without hallucinating, but here's some stuff you could try researching."

Certainly Claude's tone and "attitude" (FSVO) works much better for me than any other LLM I've tried, though mileage will, of course, vary.

(I have zero connection to the company and am still on a free account, I'm just quietly impressed relative to the competition)


I absolutely agree with your analysis wrt current tech - however, I suspect the person you're replying to is talking about "what would be really cool" in terms of it happening in a future where the relevant underpinnings had advanced to the point where it could actually manage the situational/nuance stuff properly.

I almost certainly wouldn't want to use something that tried to implement it now but it's a lovely dream and the state of the art keeps advancing at quite the speed (i.e. faster than I would have predicted, even when I do my best to take into account that it keeps advancing faster than I would have predicted ;).


It does kinda make sense to me that the various non-US subsidiaries that survived did so at least in part because those places didn't have anything like Walmart.

Whether they'll be able to continue to survive in a world containing the current incarnation of Amazon is a different question and I've honestly no idea how that will turn out.


Lots of people not in the US saying they got redirected to various places due to (I guess) some sort of GEOIP detection rubbish, me included.

There's a (just about readable, the background is greyed out due to a popup, although thankfully the popup doesn't cover any of the text) archive at https://archive.ph/3HvjM - far from perfect, and I understand if people would rather not bother than deal with the low contrast ick of the archive, but I found it interesting enough to maximise my screen brightness and read it anyway.


I got redirected to history.co.uk/ (the root, so lol, no article for you)

There's a (just about readable, the background is greyed out due to a popup) archive at https://archive.ph/3HvjM - far from perfect, and I understand if people would rather not bother than deal with the low contrast ick of the archive, but I found it interesting enough to maximise my screen brightness and read it anyway.


Once you get as far as FDIC insurance being involved, the bank generally ceases to exist (ideally via a fire sale to another, more stable bank) and the shareholders generally get (all but) wiped out, at best.

Competent risk management so that doesn't (generally) happen is a core competency for a bank, and if regulators think you're doing it wrong they will come down on the bank's leadership like a ton of bricks.

If anybody reading this comment would like to learn more from people who understand the area far better than I do, I would recommend patio11's 'Bits About Money' and Matt Levine's 'Money Stuff.'


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: