digitalmars.D - inner functions instead of scope
- cy (105/105) May 23 2016 Someone was mentioning how in RAII you can't tell if there's an
- Marc =?UTF-8?B?U2Now7x0eg==?= (10/21) May 26 2016 ^^^^^^^^
Someone was mentioning how in RAII you can't tell if there's an exception condition on destruction, but to the crazy degree that D inlines functions, I think something similarly concise is possible that solves the problem. The problem I'm talking about is when you want to clean up a resource, you have to reserve it at the beginning, then free it at the end, but freeing it might be different depending on if you're failing or not. Classic case being a database transaction. begin(); try { do_stuff(); } catch(e) { rollback(); throw e; } commit(); That's the way most people would think to do it, which has the problem that if "do_stuff" is a really huge block of code, then it's hard to remember to properly rollback and commit on the bottom. There is of course the "D" way to do it. { begin(); scope(failure) rollback(); scope(success) commit(); do_stuff(); } That's what I usually go with, because it's not too verbose, is very clear on what's going on, and related code is grouped together. Some people would like to do something like this: { Transaction somevariable; do_stuff(); } But of course, when somevariable is destroyed, you can't tell whether it's because of failure or success. But it's a really concise syntax, and isn't split up into three parts like the "scope" version or the try/except mess. So, what if we could do a transaction that did have as simple a syntax? And it hit me that a big block of code could be put in an inline function, and those are pretty much exactly what would be called for. void transaction(Callable)(Callable do_stuff) if(isCallable(Callable)) { begin(); scope(failure) rollback(); scope(success) commit(); do_stuff(); } ... transaction({ big_long_complicated_code_block(); }); Once you define the "transaction" function that way, it properly commits or rolls back no matter what is in the function passed as a callable. You could even have some argument only available from within the callable, like: transaction((session) { ... }); then you could call session.commit() if you succeeded. But that's getting pretty close to "Promise" syntax, and I think the absolute most secure way to do it is not expose that things are being committed or rolled back at all, to ensure that they are always committed or rolled back. It shouldn't be any less efficient than RAII would be, since the function passed as an argument will surely be inlined automatically. So, here's how I would implement transaction management for a database. import d2sqlite3: Database; import std.traits: isSomeFunction; Database db; static this() { db = Database(":memory:"); db.execute("CREATE TABLE foo (bar INTEGER UNIQUE)"); } void transaction(Callable)(Callable c) if(isSomeFunction!Callable) { db.execute("BEGIN"); scope(success) db.execute("COMMIT"); scope(failure) db.execute("ROLLBACK"); c(); } void main() { import std.stdio: writeln; try { transaction({ db.execute("INSERT INTO foo VALUES (2)"); db.execute("INSERT INTO foo VALUES (3)"); db.execute("INSERT INTO foo VALUES (4)"); db.execute("INSERT INTO foo VALUES (5)"); db.execute("INSERT INTO foo VALUES (6)"); throw new Exception("oops"); }); } catch(Exception e) {} transaction({ db.execute("INSERT INTO foo VALUES (2)"); db.execute("INSERT INTO foo VALUES (4)"); db.execute("INSERT INTO foo VALUES (6)"); }); auto results = db.execute("SELECT bar FROM foo"); foreach(row; results) { writeln(row[0]); } }
May 23 2016
On Tuesday, 24 May 2016 at 02:01:14 UTC, cy wrote:void transaction(Callable)(Callable do_stuff)^^^^^^^^ `scope` to avoid GCif(isCallable(Callable)) { begin(); scope(failure) rollback(); scope(success) commit(); do_stuff(); } ... transaction({ big_long_complicated_code_block(); });This technique is very widely used in Ruby, and I agree that it's really useful. It would be even nicer if we had trailing delegates, as Jacob Carlborg suggested [1]: db.transaction { // ... } [1] http://forum.dlang.org/post/modnbn$184o$1 digitalmars.com
May 26 2016