
Le 21/06/15 09:07, Vicente J. Botet Escriba a écrit :
Le 19/06/15 19:03, Niall Douglas a écrit :
Some may remember the thread starting from http://boost.2283326.n4.nabble.com/next-gen-future-promise-What-to-cal l-the-monadic-return-type-td4676039.html and that I would firstly prepare an optimally lightweight monad<T> for review here before going on to base a next-gen lightweight future-promise on that monad<T>.
Nial, I don't understand why you call this class monad. I see it much more as a generalized optional_expected that allows the user to say which exception is thorw when there is an error type and not an exception type.
IMO, the last parameter throw_error is really not needed.
You can always wrap an error code with a class that states how to throw an exception. That means that we need an additional Error concept that defines the throw_error function, e.g.
We could have a class that wraps an error with an exception
template <class Error, class Exception> struct ThrowError { Error value; };
template <class Error, class Exception> void throw_error(ThrowError<Error, Exception> const& e) { throw Exception(e.value); }
Now the generalized optional/expected class will use the throw error whenever it needs to throw an exception and has an error type stored.
BTW, why have you chosen a Callable for throw_error instead of directly the Exception?
BTH, expected could already store an error or an exception_ptr. All you need is to have a class
template <class Error, class Exception> struct ErrorOrException;
Resuming you monad class is in some way an alias of
expected<optional<R>, ErrorOrException<ThrowError<Error, ThrowException>, Exception>>
possibly optimized with a specific specialization.
BTW, why your class accepts only on Exception type and not a variadic set of Exceptions types?
I have some trouble with the is_ready function. You say "True if monad is not empty.". Do you mean that oi will not be ready
optional<int> oi;
You need to add the preconditions of each function. No precondition mean the function is not partial.
I don't see comparison operators, neither hash customization, was this intentional?
I suspect that the exception thown by value() when an instance is empty is not future_error<no_state>,and that there is a type on the documentation. Or was this intentional?
In the function value()&& you say "If contains a value_type, returns a rvalue reference to it, else throws an exception of future_error(no_state), system_error or the exception_type."
Why system_error? Do you mean that when the exception_type given as parameter is exception_ptr, the exception throw is exception_ptr and not the stored exception? In the Expected proposal and in std::future, the exception throw will be the stored exception.
The function has_exception is not coherent with the others state observer functions empty/has/value/has_error.
What is the exact signature (or valid signatures) of the functions passed to map/bind/then? BTW, are these functions proposed?
Last can the map function be applied to an empty instance? What would be the result?
I would like to suggest a different approach. Given a class optionals<T1, ..., Tn>, which is a variant accepting to be empty we can see optionals<T, E1, ..., En> as an Error Monad, when all the Ei are instances of the Error concept. A class is a model of the Error concept if it defines the rethrow_exception function. When the optionals is empty the exception thrown is bad_optionals_access. exception_ptr is a type erased Error that rethrows the stored exception. std::error_code and std::error_condition can be seen as instances of the type class Error if we define the rethrow_exception on them throwing an system_error exception. optionals<T1, ..., Tn> can not define all the function that your class monad defines as they are too specific. Instead of defining member functions we can define non-member functions. has_value/has_error/value/set_value/set_error/get_error/emplace/map/bind/then optionals<T1, ..., Tn> should be default constructible from Ti and convertible to Ti using maybe a specific cast function. optionals<T, E1, ..., En> must be implicitly convertible from the result of throw_error(e). We could also define a specific class as you propose with specific semantic for the first argument that would defines member functions. I'm not against this approach, nevertheless the main issue I have is how to name this class :( result<T, E1, En> doesn't conveys the fact that it can be empty. monad<T, E1, En> is not good as Monad is a type class not a concrete type. optional_result<T, E1, En> is too long optional_value_or_errors<T, E1, En> is too long HTH, Vicente