5 ms·
"There is nothing exceptional in exceptions" No there's not, but it's a royal pain the arse to have to keep passing them up through your function calls to the
by brango 9y ago
"There is nothing exceptional in exceptions"
No there's not, but it's a royal pain the arse to have to keep passing them up through your function calls to the level that actually cares about them and will do something about them. Try/catch eliminates boilerplate.
- ordu 9y agoDoesn't Go have any syntactic sugar for pass errors out of function? Something like Rust's Carrier/From traits and try! or `?`.
- camus2 9y agoNo, go "errors" are just a convention, there is absolutely nothing special about them. Go has panic/defer, which are exceptions, but done badly. They were probably retrofitted in the language when its creators realized they needed exceptions anyway, which makes the whole "error as value" a bit hypocritical. If they truly cared about error as value, then yes, Go would support some form of try! macro.
- stevedonovan 9y agoYes, Rust would be seriously irritating without try! (and its short friend the question mark operator). So I feel the pain of gophers here.
- pjmlp 9y agoNot really, Go community tends to be very vocal against syntactic sugar.
- jstimpfle 9y agoAnd even with exceptions, C++ is the only mainstream language I know that has a story how to undo any investments so far, including those that are not memory allocations. In any other language, you have to at least write wrappers that execute commands and catch any exceptions to do specific cleanup actions (like aborting a database transaction). And in C++, while it has a story how to do that, implementations must implement their own state to decide whether the reason for quitting is success or an exception. (this is more elegant in Haskell, which can do this with sum types, and for example monads on top).
- alphaalpha101 9y agoI don't really agree that it's inelegant to decide whether the reason for quitting is a success or an exception, or whether it's necessary. Like, you've either committed, in which case you shouldn't need to do any cleanup, or you haven't committed, in which case you need to clean up, right? If you have an object representing some sort of transaction, and you just have actually-do-all-the-work-at-once-on-commit-being-called semantics, you don't actually need to do anything in your destructor at all, right? template<typename T> class transaction { std::vector<T> things_to_do; public: void add_work_item(T t) { things_to_do.push_back(t); } void commit() { for (auto t : things_to_do) { t.do(); } } };
- jstimpfle 9y agoYou are just making my point with that OOP mess (sorry). You are not using RAII to finish (meaning cleanup or rollback) the transaction. What we want actually executed in the end (in terms of control flow - not necessarily what we would be willing to code), is something like the following clean procedural code. do_some_foo(): start_transaction() r = do_thing_A() if r == CONFLICT: rollback() return r r = do_thing_B() if r == CONFLICT: rollback() return r commit() return OK
- alphaalpha101 9y agoThere's nothing remotely object-oriented about what I wrote. I don't see any inheritance. I don't see any polymorphism. I don't see any virtual member functions. And of course I'm using RAII to roll back the transaction. When the object is destroyed the vector is destroyed and the transaction isn't run. It doesn't make any sense at all, quite frankly, to use RAII to do what you wrote there. That's not what RAII is for, nor is it what RAII is good at. RAII is for managing resources.