Le 24/05/2017 à 21:44, Thomas Heller via Boost a écrit :
Hi Niall (and also probably Vicente) I believe that this should be discussed probably in the std-proposal ML. Anyway as we are here I was following the discussion about expected/outcome/result very closely and I can absolutely see the usefulness of such a library (I explicitly discarded option here).
I am not sure if this post should count as a review, mainly because I disagree with the fundamental design decisions (that includes expected as defined in D0323R2) and therefor would cast my vote as in "not ready yet", I hope I can convey my points below. Wow, first notice I have of this disagreement. It is better later than never.
First of all, I don't agree with the strong (conceptual) relationship between optional (be it boost:: or std::experimental) in such a way that expected is a generalization of it. From what I understand, the purpose of expected/outcome/ result is to be used as a mechanism to return the result of a function. As such it should also expose the semantics of it. Fortunately, we already have a (asynchronous) return object standardized (std::future). And this is my basic disagreement here: Why not model expected in the lines of future? With the main points being: - expected being movable only - expected
::value() should always return by value and invalidate this. - (I would really prefer the color .get though ;)) So the question to Vicente/Niall is: what is the motivation to make it "optional-ish"? Do we have use cases which make this design undesirable? expected is a generalization of optional as it is synchronous and could return more information about why the the value is not there. expected could be seen as the ready storage of a future. future::get block until the future is ready and then returns by reference :)
I want to ask you, what would be the liabilities of an expected that is copyable? We don't have a problem returning be reference, why would we like to return by value? Why do you prefer get? what do you get with get? How will you name the function that give you access to the value of a PossiblyValued type?
By having these constraints, expected of course needs to have an uninitialized state. As such we'd have the three observers: valid(): true when has_value() || has_error(), false otherwise (for example default constructed, invalidated), has_value() and has_error().
Sorry but, not. We don't need such state. This is something future needs, but not expected. I we decided to default construct to an uninitialized state, I wouldn't support to show to the user this state via any observable function, but via UB (as for chrono::duration).
Second, I think Niall raised a valid about outcome being a framework for interoperability (completely orthogonal to the first point). However, I totally miss this from the proposed library, most pressing are non intrusive mechanisms. For that purpose I postulate, that a mechanism to transform between different unexpected results, that is: various error codes etc. However, for that to work, one would of course need a properly defined concept, for example, as Vicente suggested "EitherValue", and a mechanism to coerce one error type into another, maybe through ADL, or traits specialization or whatever.
With that in place, one could simply define the different EitherValue types, there is no need that everything needs to be in the form of "basic_XXX". For the library under review, this would be perfectly sufficient: template
class expected; template <typename T> using result = expected ; template <typename T> using outcome = expected >; We will need to have a specialization of expected > as we have an index for variant. The revision 2 of the expected proposal talks of a expected . That is, given that we have either a value or unexpected, we can convert expected
to expected if T is convertible to U and E1 "coercable" (with whichever mechanism) to E2. I have added this conversion constructor recently to the expected
There will be such a proposal as a generalization of Nullable based on what I named PossiblyValued.. https://github.com/viboes/std-make/blob/master/doc/proposal/nullable/D0196R3... proposal as the result of my understanding of the need of Outcome and I hope we will discuss it in Toronto.
If we then have a generic mechanism to get from a (possibly user defined "E") to an exception, I completely miss the point of the outcome template.
And why not to throw E?
Furthermore, I believe that .value/.error should really have a narrow contract, that is that it is UB to call those functions if the respective types are not held (easy to catch this in a debug build...). Why? Probably just a micro optimization, but consider the canonical usage: auto r = some_function_returning_expected(...); if (r.has_value()) // Do something with the value else // An error occurred, PANIC In both branches, we know exactly what's in there ... so why check again when getting the state out? I don't get the "reinterpret_cast" argument. I am one of the persons that believe that UB is a necessary evil for some optimizations...
We agree here.
All optimizations can then easily be put as implementation details and the generic expected
will probably suffice for most use cases, for everything else, we can implement special types which conform to our concepts and implement the error conversion mechanisms. This will most likely also work with different APIs/ABIs.
The main problem is that we don't have here the generic interface and it is for this reason we are discussing on the details of a concrete class. The original expected proposal has fmap, bind, catch_error functions. We have removed them form expected since the last revision, but we need now to have a generic interface for those functions. For me expected should have the minimum, everything else should be associated to a specific concept, as Nullable, PossiblyValued, MonadError, SumType. https://github.com/viboes/std-make/blob/master/doc/proposal/monads/Monads.md https://github.com/viboes/std-make/tree/master/include/experimental/fundamen... https://github.com/viboes/std-make/tree/master/include/experimental/fundamen... https://github.com/viboes/std-make/tree/master/include/experimental/fundamen... Vicente