Formal empty state remains for outcome<T>, result<T>, option<T>. As I've hopefully shown by now in other discussion threads, the formal empty state is very useful, saves a lot of boilerplate. For those not wanting the empty state, there is expected
. Okay, understood, even if I don't agree, at least for result<T> and option<T>. For outcome<T> I have my doubts, I don't know if the never-empty warranties can be implemented in an efficient way.
Why do you think outcome<T> cannot implement a never empty warranty? Perhaps you are thinking of std::exception_ptr's very unfortunate lack of exception warranties on its move and copy constructors? I agree that the lack of definition by the C++ standard there is deeply unhelpful. Older STLs also didn't implement a move constructor because the standard doesn't require one, and marked the copy constructor as throwing. Very unhelpful. But recent STLs appear to have since added a nothrow move constructor. I think we are safe-in-practice rather than safe-through-design.
raw_value_type, raw_error_type and raw_exception_type are the true, original type configured. You are correct that this is a deviation from LEWG Expected which would cause code written for LEWG Expected to fail to compile with Outcome's Expected. I think that safe. If the raw_ are the reason d'ĂȘtre to avoid the void specialization, and this avers to be a useful technique, I believe it merits a full implementation section explaining how this improve the compiler performances, DRY, et all. I had thought it a very common technique, so common that it not worth explaining here. Maybe you can point me to some blog, mail exchange, ...
Am I wrong on this? Possibly. What is clear to you is is not forcedly to other.
Ok, people please tell me if this technique is unknown to them:
template
The answer is that we currently don't. The seamless interop applies to within Outcome's island of types only. However it is very straightforward to write converting constructors to construct the std::* editions into Outcome editions. Getting them back out is on the end user, though I could be persuaded to write an explicit conversion operator for the std::** editions.
So if the interaction is 3pp libraries is by defining conversion operators, why do we need Outcome. We could define already those operators without making the design more complex.
The intention is to provide interaction with *STL* supplied alternatives via this means. That's because we can't adjust the source code of STL types. Third party code where the author may modify the source should supply a custom policy class to basic_monad.
For example I'm proposing a ProductType concept to the standard. Each product type could define when it converts from any ProductType satisfying certain properties. This will simplify and extend the current tuple-like interfaces with less code and more software.
I believe we could define a PossiblyValued type of classes and define conversion from them in a non intrusive way. Note that I used PossiblyValued and not MonadError as the required interfaces wil not be the same. A PossiblyValued value type that is close to Nullable, but where the not-a-value is considered an error and can have multiple instances. optional and expected and any the smart pointer I know will be PossiblyValued types.
Let me know if you consider this design on the ivory tower and a weird design.
I think this is all great stuff for pondering for future C++ standard libraries. But not strictly relevant to this Outcome review.
That would be the traditional way of implementing this. I chose not to follow that design in this particular case.
I've no problem as far as the common implementation is hidden and doesn't guide the concrete interface. I believe that generic programming must be built on top od concepts, not inside a intrusive class that know how to do everything.
I think you may not have studied the source code sufficiently.
basic_monad definitely does not know how to do everything.
basic_monad takes a policy class to flavour it with personality. The
policy class is a type full of typedefs and constants setting up what
this particular basic_monad will do. The storage layout is strictly
separately defined by policy than the public member functions mixed in
by the policy to the final basic_monad class instantiated.
So, you could use a std::variant for the storage layout, or three
std::optional<>'s, or three unique_ptr<>'s, or more excitedly, a
std::expected
Niall, I'm not against not for you library. We're just reviewing it now. For me the goal is to improve it, and if we can at the same time improve the std expected proposal this will be very valuable to the C++ community. I would be very glad if this process helps the Expected proposal. Expected is a great proposal, it needs into the C++ standard.
It is weird, that you say that it is a great proposal, but you found something that must be added, modified or removed. I would like to understand these differences, because surely the expected design must be improved when I would take into account *valid* use cases that I have not considered yet.
I nitpick tiny corner case problems, but I consider the overall proposal to be great. I do thank you personally in Outcome's Acknowledgement for all your service in making Expected happen, without all your work Outcome would not exist and my other C++ libraries using Outcome would be the worse for it. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/