3 ms·
RAII (i.e. destructors that automatically run on values when leaving a scope) is what lets one avoid manually freeing Box and Vec, so you want to split the worl
by dbaupp 10y ago
RAII (i.e. destructors that automatically run on values when leaving a scope) is what lets one avoid manually freeing Box and Vec, so you want to split the world into "destructor resources" (RAII) and "with resources", maybe with some guidelines for when to choose which.
Presumably a data type that contains a "with resource" would itself have to be a "with resource" (or else why bother?). This makes "with"-ness abstraction-piercing and infectious, so, for backwards-compatibility, if there was any chance of a type eventually containing a "with resource", the programmer would have to mark the type as such a resource preemptively, or else be forced to make a breaking change to their library. All for a minor syntactic difference in how one constructs values.
Also, how does one return values from `with` clauses? It's useful to be able to write a function that returns a file, so one would need yet more machinery, just to be able to cancel the normal with-cleanup. Again, all for a relatively minor syntactic distinction.
- pjmlp 10y agoNot necessarily. On languages that support currying, or leaving the last parameter out of the call if it is a lambda, you can create high-order-functions for resource management. The effort is no different than implementing RAII classes. withDBConnection { r = db.execute ("SELECT....") } Here the function withDBConnection calls the lambda, providing it an implicit parameter for the db connection, which it fully controls. There are more fancy ways to improve on this, specially if the language also allows for macros. So I find a bit sad, that the discussions of with/using/try don't focus on the FP way of doing resource management.
- dbaupp 10y agoThat has both of the problems I mentioned even if it isn't a full language level construct.
- pjmlp 10y agoI fail to see how. The resources are only available via the lambda parameters, so the wrapper function controls the lifetime, arena style. Last expression in the block is the return value.
- dbaupp 10y agoThe wrapper function controlling the lifetime is exactly why that scheme has both of the problems I mentioned. It all comes down to: how do you write a fn connection() -> DB? You can't (or, at least, it doesn't make sense to) return the parameter from withDBConnection: the whole point is the wrapper function does the clean-up when it is done, invalidating the connection object. The only way to pass the value along is to have `connection` also take a closure, i.e. manual continuation passing style/node.js's "closure-hell". This can, in some cases, be addressed with async/await style sugar, but there's still semantic (mainly around composition) and performance issues with it. Anyways, going back to the data type: if one has a type Foo with a constructor like fn create() -> Foo, and Foo is updated to contain a database connection, that constructor no longer works, and needs to also be converted to use CPS. That is, the interface of the Foo object has be infected by "private" details, meaning needs to to preemptively decide whether there is any chance of each datatype needing to hold one of these CPS objects. (Of course, the code you suggest works fine in Rust now, and is even used for certain special cases, but that is very different to baking it into the language as a whole new syntactic construct.)