Subject: Re: [boost] [outcome] High level summary of review feedback accepted so far
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-05-28 20:43:49
2017-05-28 11:35 GMT+02:00 Vicente J. Botet Escriba via Boost <
> Niall I'm trying to see what you want to propose by difference to a
> hypothetical generic interface to see what is the added value.
> Le 27/05/2017 Ã 16:13, Niall Douglas via Boost a Ã©crit :
>> - outcome<T> and result<T> will have their empty state removed, and all
>>>> observers gain narrow contracts. Default construction is disabled.
>>>> https://github.com/ned14/boost.outcome/issues/44. Example:
>>>> result<Foo> v(make_errored_result(std::errc::invalid_argument));
>>>> assert(v.has_error()); // true
>>>> assert(!v.has_value()); // true
>>>> // As if Foo(reinterpret_cast<Foo &&>
>>>> // (error_code_extended(std::errc::invalid_argument));
>>>> Foo f(std::move(v.value()));
>>>> assert(v.has_error()); // still true
>>>> assert(!v.has_value()); // still true
>>> Just a question I could do for the existing library also. What has_error
>>> mean for outcome, result? is that it has an EC or that it has no value?
>> .has_error() is true if and only if the current state is an
>> error_code_extended instance.
> Okay. Why don't you name it has_error_code?
> For me having an exception_ptr stored was also an error.
> Why not name the getter error function get_error_code, .... as the
> function doesn't returns a generic Error but one of them.
> I don't think using the same name for different things is helping in
> generic programming.
>> And now that we don't have empty, what is the sens of has_error for
>> There will be no longer an empty state in result<T>, but for generic
>> programming I'll be retaining a .has_error() so end users can more
>> easily swap a result<T> for a result_e<T>.
> I see generic programming in other terms.
>> Maybe, outcome should have a get_state function that returns an enum so
>>> that the user can do a switch.
>> I had previously a .visit(callable) for that.
> Any sum type should provide a visit function. I've implemented it in the
> std_make repository. I will add it to Expected proposal once we have a
> SumType type of classes.
>> (NOTE: expected<T, E> will track whatever LEWG Expected does, but it
>>>> differs from what will become result<T> by having a wide contract on
>>>> .value() and narrow contracts on operator*(), .error(), operator->().
>>>> result<T> will have narrow contracts on everything, it is basically a
>>>> thin wrap of std::variant<T, error_code_extended> except with strong
>>>> never empty warranty)
>>> What will be the differences between result<T> and expected<T,
>>> The wide contracts for the observers? Can not we provide wide and narrow
>>> contracts or don't reuse the same name with different meaning?
>> The description above quite literally tells you the differences.
> Why we cannot provide wide and narrow observers? If this is the single
> difference I don't see the need for having two types.
>> If we had a expected<T, E1, .., En> what will be the differences between
>>> outcome<T> and expected<T, error_code_extended, exception_ptr>?
>> I would assume an expected<T, E1, .., En> could only return a
>> std::variant<E1, ..., En> from its .error(). I can't think what else it
>> could do.
> This function *could* return variant<E1, ..., En> or variant<E1&, ...,
> En&> or any sum type that represents the error, that is the Not-A-Value.
> Maybe, expected<T, E1, ...>::get_error<Ek> could be more useful.
> In any case expected<T, E1, ..., En> should provide a visit() function.
> Having more than one Error makes this absolutely necessary.
> When having more than one error in expected, having access to each one of
> them using a different interface would make the user code more complex,
> isn't it?
> outcome<T> continues to provide:
>> - T& .value()
>> - error_code_extended .error()
>> - std::exception_ptr .exception()
>> i.e. hard coded.
> Ok, so the single difference would be about the error observers returning
> by value and having an explicit name.
>> - New typedefs outcome_e<T> and result_e<T> are identical to outcome<T>
>>>> and result<T> except for adding a formal empty state. Observer contract
>>>> slightly widens, an attempt to use an empty object throws a
>>>> bad_outcome_access exception. Implicit conversion from non-empty-capable
>>>> varieties is permitted to empty-capable varieties, but not the other way
>>>> round. Default construction is to **empty**.
>>> Okay this corresponds to what others are naming optional_outcome,
>> I'm not wedded to result_e<T> etc. Ok, I'll change result_e<T> and
>> outcome_e<T> to optional_result<T> and optional_outcome<T>. Done at
> Neither me. Just wanted to name the types in a more explicit way. result_e
> doesn't tells me the intent.
>> If we had a optional_expected<T, E1, .., En>
>>> what will be the differences between result_e<T> and
>>> optional_expected<T, error_code_extended>?
>>> what will be the differences between outcome_e<T> and
>>> optional_expected<T, error_code_extended, exception_ptr>?
>> I am not sure what semantics you have chosen for optional_expected<>.
> optional_expected<T, ... > should be an optimize version of
> optional<expected<T, ...>>.
According to Niall's example, it was my understanding that his mental model
was `expected<optional<T>, ...>`. That is, empty resutl means: no error
ocurred, I successfully computed the non-value.