Similarly to the value returned case, Outcome does not involve itself into whether a T value returned is valid or not in exactly the same way as it does not involve itself into whether a E value returned is valid or not.
If you are saying "I just give you a tool for storing either T or error_code or std::exception_ptr or nothing, and I do not care how you use it", I can accept that. But the semantics of `o.error()` seem to contradict that a bit: as though you were in fact trying to workaround for potential mis-usages of your library.
I very much came at the default actions as a means of saving the programmer from typing boilerplate. The specific actions I chose have been mostly the same since day one. I think there was one which turned out to be a mistake during the past two years of using the library in my code, I changed it. Otherwise they've been tested in the real world for a quite a while now.
Given what you already said about semantics of function `error()`, I consider the documentation of this function insufficient: https://ned14.github.io/boost.outcome/structboost_1_1outcome_1_1v1__xxx_1_1p...
Good point. Logged to https://github.com/ned14/boost.outcome/issues/26
(BTW, note that there is something wrong with the links. If I click on it, I do not get any more details for `error()` but instead get "Detailed Description" of boost::outcome::v1_xxx::policy::monad_policy_base)
It worked here fine.
Anyway, the short description of function `error()` says, "Returns any errored state in the transport, throwing an exception if empty."
1. I wish you didn't use this word "transport" as a noun. It always confuses me. Do you mean "either `option` or `resutl` or `outcome`"?
Transport is a noun meaning a device which conveys something.
2. "any errored state"? -- not the specific error state previously set inside `outcome`?
If we refer to state, we mean the variant in Outcomes.
3. It does not mention your algorithm: if `has_value() == true`, returns a value-initialized error code; if `has_exception() == true`, returns `error_type((int) monad_errc::exception_present, monad_category())`
4. "Throwing exception if empty" -- what exception?
All the above is covered in the tutorial, but I agree it needs to be in the reference docs too. It will be fixed.
I trust you that all these additional guarntees cost nothing at run-time. My concerns are not really about a potential run-time overhead, but about what is a correct usage of the library and what is a buggy usage. For instance, if you changed the semantics of function `error()` to:
Requires: `has_error() == true`. Returns: the error_code stored in `*this`.
This would make the understanding of the interface simple, it would clearly indicate when the users do something wrong, you could still implement your "rescue semantics", but I when I am doing the code review for my colleagues, I have something objective to rely on: "hey, you are breaking the precondition, you are extracting the error even though it is not there". Now, with the rescue semantics, I cannot say a word in the code review because the other programmer will respond, "But I learned the detailed rescue semantics, and I figured out it is exactly what I need." <-- the code does what the programmer intended, but is difficult to maintain, because it relies on the rescue semantics.
By "rescue semantics" I mean, "id you do not have an error_code to return, just fabricate one".
I get what you're saying. The fact there are no preconditions on .error() means that calling it is always a valid thing to do, and not by definition a bug nor a code smell. This design is intentional. *If* you learn off the "rescue semantics" as you put them, then when writing code you swap tedious boilerplate for code which relies on those rescue semantics. If you feel that a woolly and imprecise step too far, then you can use expected<T> instead. It's why Outcome ships with expected<T>. Each of its observers come with strict preconditions governing whether the call is valid or undefined behaviour. In the end it's up to each end user and their use case. If an end user does a lot of code correctness auditing and peer review, depending on that team's style they may prefer the boilerplate to always be spelled out for easier auditing. Or they may prefer tighter, denser code so more logic can be reviewed per screenful at once. It really does depend on the team, the code base, and what you're looking for. As I've mentioned in other threads, Outcome really is my only library where I give people - including myself - a choice in what style or use pattern or convention to use. Because every team, code base and company is different. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/