Not the main point here, but I think you could almost fix the defer approach with types. Instead of:
File f = File.open(...);
defer f;
You could have:
PleaseDeferMe<File> p = File.open(...);
File f = defer p;
If you want to actually access that file you've created, your only path is through using defer.
One case this doesn't solve is creating an object but never using it. When writing new code, you probably won't make that error, but when ripping out old code you might miss a piece you should have deleted. If the compiler warns about unused variables (for every possible code path), it could catch that too.
(This could be generalized a bit. Call it CleanMeUp<T>, have a CleanMeUp.takeResponsibility() method that returns T, and maybe defer isn't the only thing that can call takeResponsibility().)
Java’s `throws` keyword comes to mind. It forces you to handle exceptions or explicitly declare that you expect the exception to be handled by a method higher up the call stack.
Your strongly-typed suggestion seems better. But you’d have to somehow allow for cases where a function opens a file, does something with it, and also expects the file to be closed by a higher function. Perhaps something like:
PleaseDeferMe<File> BeginWrite() {
PleaseDeferMe<File> p = File.open(...);
// unsafe! Make sure to return p to the calling function so this gets called with “defer” at some point
File f = unwrapDefer p;
f.Write(some header)
return p
}
One case this doesn't solve is creating an object but never using it. When writing new code, you probably won't make that error, but when ripping out old code you might miss a piece you should have deleted. If the compiler warns about unused variables (for every possible code path), it could catch that too.
(This could be generalized a bit. Call it CleanMeUp<T>, have a CleanMeUp.takeResponsibility() method that returns T, and maybe defer isn't the only thing that can call takeResponsibility().)