I’ve never truly understood why we could explicitly commit a transaction, but we could only implicitly roll one back. There is a universe of difference between throwing an error (and ending the call stack), and rolling back (and continuing execution).
There was always a way to roll back and go on, sure. Wrap the entire thing in a if Codeunit.Run() block, throw an error as the last thing inside that codeunit, and there you go. Problem solved. Well, not quite.