First of all, I like where Appendix C is going. My (small in comparison) problem is with C<let>'s definition of "failure." The exception bit is neat, and provides for pretty much out-of-the-box exception safety. But failure based on return value may not be such a hot idea. Consider:
class DBHandle { method fetch($key) { return %.cache{$key} if %.cache.exists($key); return let %.cache{$key} = .expensive_fetch($key); } has %.cache; } Presumably, if C<expensive_fetch> throws an exception, C<%.cache> will be unchanged. Additionally, if C<expensive_fetch> returns C<undef>... perhaps because the database says the associated value is undefined (but exists nonetheless), C<%.cache> will I<still> be unchanged. This results in an expensive fetch operation each time. There's two solutions that I see. The first is to only rollback C<let>s on returning undef or () when C<use fatal> is not in effect. If one wants to sucessfully return undef in these situations, one returns C<undef but true> or C<undef but defined> (!). Another solution may be to mark C<undef>s that signify failure with a C<failed> property. That way you can return sucessful C<undef>s without fear of contradiction. But this leads into something related that's been bugging me about C<use fatal>. What happens in this case? package Foo { use fatal; sub oof() { try { bar; CATCH { ... } } } sub bar() { baz; } sub baz() { fail; } } no fatal; Foo::bar // die; Obviously, C<baz> throws an exception to C<bar>. However, if C<bar> subsequently throws an exception, the main module won't be expecting it. However, Foo::oof will be expecting it, and won't settle for C<undef>. So, in conclusion, C<use fatal> governs not what subs under its control I<return>, but rather what is returned I<to them>. This can be trivially implemented with C<caller> or, if inlined, simple transformations. Does this idea have any merit? And I've got lots of nifty ideas about C<caller>, Coming Soon to a mailing list near you! Luke