| > You really should be able to express the required cleanup semantics with "defer" I'm perfectly able to do that, I just don't want it, because the result is terrible. The syntax no longer shows what happens when and where. And, if you look at n3434 <https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3434.htm>, you can see the following gem: > the deferred block may itself contain other defer blocks, that are then executed in chain according to the same rules This is the worst possible outcome. It means that a continuation passing style sub-language gets embedded in C. {
defer { a(); defer { b(); }; c(); };
defer { d(); };
}
At the final closing brace shown, d() will be invoked first, then a(), then c(), then finally b(). Syntactically, the invocations appear in a-b-c-d order in the source code, but the actual execution is neither that nor the inverse d-c-b-a nor the single-level inverse order d-a-b-c.THAT is an unreadable mess. Good luck debugging that. Not dissimilar to chaining futures in modern async C++ (lambdas deeply nested in lambdas). Which is an abomination. Your source code syntax is now completely detached from the execution order within a single thread. Good luck getting that past code review. Your reference to "no take only throw" makes no sense to me. The gist of that meme is "various characters making contradictory demands". Nobody is making demands here. There's no need for an "explicit alternative". Just don't defer at all, regardless of style. Write the error path / exit path explicitly, using gotos or the arrow pattern. |
There is a style of grey-beard that never wants to reason in parts. He simple loads the whole program into his big brain and works from there: trees through the eyes, forest (maybe, certainly subjective) in the mind.
I too am big-brained, but I wholly reject this approach --- it makes for code that is sorely lacking in motivation --- all "what", no "why".
With "goto" I need to reconstruct what the arbitrary control flow means. It might be cleanup, it might be something else entirely! With "defer", we have the essence of RIAA without any bad OOP nonsense. The cleanup obligations themselves are literal in the code.
You don't need to know the order d, a, c, b run in 99% of the time. The reverse-order semantics indicate the LIFO semantics, which should be enough and tied to e.g. the deadlock avoidance or inter-resources-that-need-cleanup dependency management. And again, whenever you do want to see what the actual order is, the desugarer should always be a click away, perfectly your questions.