2017-05-20 20:01 GMT+02:00 Niall Douglas via Boost
I am not trying to propose any new logic here. I am trying to assign some mental model to what `outcome<T>` currently does. Consider: some funciton `f` is supposed to compute an `int`. But the computation may fail, in this case I want the function to return the reason why it failed. Also, it is possible that I will get some "internal error". For this reason funcio `f` returns `result<T>`:
``` result<T> r = f(); ``` Now, `r` can represent the following states:
``` if (r.has_value()) // value successfully computed {}
No, the correct interpretation here is that the result returned has a value, not that the value was successfully computed.
else if (r.empty()) // internal error {} else if (r.error()) // reason why f failed {} else if (r.has_error() && !r.error()) // what is that??? {}
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. 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... (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) 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`"? 2. "any errored state"? -- not the specific error state previously set inside `outcome`? 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?
The last case is neither a value, nor a reason for failure, nor an "internal error". How should a caller interpret it? Two possible answers:
1. There is a fourth state with no intuitive or sane interpretation. 2. Assume this never happens. If it happens it is a bug in calle site that needs to be fixed.
You've got to remember all these potential state flow paths never occur in any real world code base. You'll even see the compiler eliding in the assembler output any handling of an empty state being returned if it can see that that is not possible.
Same goes for if you return only ever a value. The result<T> effectively "disappears" in the assembler generated, and it is as if you returned a T directly.
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". Regards, &rzej;