4 ms·
"using" as specified is too OOP for my taste, i think i prefer "defer" like in Golang. let connection = getConnection() defer connection.dispose() ... b
by _old_dude_ 3y ago
"using" as specified is too OOP for my taste, i think i prefer "defer" like in Golang.
let connection = getConnection()
defer connection.dispose()
...
being equivalent to
let connection = getConnection()
try {
...
} finally {
connection.dispose()
}
"defer" unlike "using" does not require a specified entry point method, so no [Symbol.dispose] required.
- duped 3y agoYou're pawning complexity off to the caller of `getConnection` with `defer` semantics. The only time `defer` is preferable to RAII is when the release of a resource to its corresponding acquire may have a different set of arguments depending on the context. The example you've given doesn't really seem that compelling. acquireX/releaseX maps nicely to lexical scope and adding a language feature to provide sugar for it is pretty nice, imo/e. Defer doesn't really "get it."
- computomatic 3y agoThe salient point from the parent comment was that “using” assumes the resource is an Object. The core value prop of both “using” or “defer” is that a single scope can manage the lifetime of some resource while child scopes interact with that resource. “Using” assumes that resource is a local object but that’s rarely actually the case - as in the example of a DB connection - it’s just how OOP represents it. A good counter-example would be if I need to manage a resource via an API. Maybe one request creates the resource and another tears it down. (Eg, begin a transaction and then commit it). “Using requires me to implement a new class to represent this resource - that’s OOP at its finest - whereas “defer” allows just doing the thing (and works equally well for objects)
- Timon3 3y agoThere is no need for you to create a new class to represent a resource. Javascript doesn't require objects to be instances of specific classes, so you can just return an object with the dispose method while only using it as a container. Even when using Typescript there is no need to add a new class, a new type declaration is more than sufficient. Just because it's on objects doesn't mean it's OOP.
- Dylan16807 3y ago> “Using” assumes that resource is a local object but that’s rarely actually the case - as in the example of a DB connection - it’s just how OOP represents it. Can you elaborate on this more? It would be hard to represent a DB connection without an object somewhere (or a closure that's effectively the same thing), and I don't understand how local or not comes into play. > Using requires me to implement a new class to represent this resource It just requires you to set a key. You could also have a function to add it or do it locally. Locally setting up Symbol.dispose does require more boilerplate than `defer`. But if the dispose is built into the object then `using` is less boilerplate than `defer`. And if you use a function to add the disposal, `using` with an extra function call is also less boilerplate than `defer`.
- _old_dude_ 3y agoDefer is more general than using, you can use it for other use cases, for logging logger.log("start") defer logger.log("exit") ... for keeping alive a reference (because you need to call C code) global.keepalive = myvar defer global.keepalive = undefined ... // call C code here etc
- Dylan16807 3y agoThose seem like things you'd do multiple places and it would be a good idea to make them into a function. If you have a one-off, defer is nicer looking but using is perfectly capable of doing the job: function defer_it(callback) { return { [Symbol.dispose]: callback }; } // will look better with using void using _ = defer_it(()=>logger.log("exit")); Or use DisposableStack() as another comment in this thread mentions.
- throwaway12311 3y agoI'm honestly okay with either of these. try-finally has the unfortunate indentation tax. This has even led me to recreate the bracket pattern from haskell in JS/TS.
- WorldMaker 3y agoThe nice thing is that the `defer` pattern can be easily built on top of the `using` pattern, and indeed the `DisposableStack` and `AsyncDisposableStack` objects also included in the TC-39 proposal for built-ins directly support it: let connection = getConnection() using stack = new DisposableStack() stack.defer(() => connection.dispose()) The proposal even notes the `defer` method name was borrowed from golang. Though in practice I think the `adopt` method will be the nicer, more common pattern for transitional things before `[Symbol.dispose]` becomes more widely utilized: using stack = new DisposableStack() const connection = stack.adopt(getConnection(), c => c.dispose())
- paulddraper 3y agoNah. Why does the caller have to figure out how to dispose? Let the callee say how to do that. let connection = getConnection() defer connection.dispose() # dispose? close? finalize? quit? vs await using connection = getConnection(); Same with C++ RAII. Connection connection();
- konart 3y ago>Why does the caller have to figure out how to dispose? Rather than how the caller have to figure out when to dispose. Defer\using will wait till we out of scope or till we return. While it may be much better to close\dispose as soon as possible or after some event.
- mmis1000 3y agoYep, this merges countless of way to dispose a resource into a single interface. Like what promise did to callback functions. (And promise almost take over plain callback these days) Even you don't use the 'using' sugar. You may still be benefited from it if you need to implement some sort of resource management someday. Instead of write a tear down function for every single type of resource in your code. Probably everyone will just agree upon that `Object[Symbol.dispose]()` will free this resource. And you only need to do it once and for all.
- pavlov 3y agoI sort of keep expecting we'll eventually have C++ where it's just: Connection. ...and it gives you an instance in a local variable with the lowercase initial to match the type. Like the "Churchill Martini" where he — according to legend — asked the bartender to pour the glass full of gin, and then he'd nod at the vermouth bottle on the shelf and say "Vermouth."