Boost logo

Boost :

Subject: Re: [boost] [outcome] High level summary of review feedback accepted so far
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2017-05-31 20:58:13


Le 31/05/2017 à 02:01, Gavin Lambert via Boost a écrit :
> On 31/05/2017 11:47, Vicente J. Botet Escriba wrote:
>> Le 31/05/2017 à 01:14, Gavin Lambert a écrit :
>>> That's unavoidable for an empty state. It's like a NULL in a
>>> database or the none_t state in an optional -- it's up to whoever
>>> uses it to decide "the value is missing, and that's bad because it
>>> should have had one" or "the value is missing but that's ok, it
>>> doesn't have to have one".
>> When I define functor::map/transform or monad::bind/chain, I need to
>> know what is the value_type if the functor or the monad. If empty is
>> part of the success alternatives it is part of the value_type. If
>> success is part of the failure, it isn't part of the value_type.
>> If we don't define if it is a success or a failure we are unable to
>> define these functions.
>
> Why do we care about monads?
>
>
We care about extensibility of the proposed classes, isn't it?

We should care about Monads, because because the functions returning
outcome or result are monadic functions and these functions don't
compose well without the monad interface.
This will avoid writing flat code. No raw loops.

When you compose function that return a Monad instead of the value type
(and in this case outcome<T> and result<T> are wrapping the value type
in one way or another), your functions don't compose any more. You need
another kind of function composition provided by a monad interface
(monad::compose())

One thing is essential. To identify what do we consider the result when
computation succeeds. If empty means success the value type is
something like optional<T>. If its is a failure the it is T. Letting the
user an interpretation of empty doesn't helps in this task. We should
have both cases with different names.

>
>> Why this is worse? We use the type system to prevent from user error.
>
> Having no exception is not an error. Asking for the exception is also
> not an error, because the exception can represent a state that means
> "I don't have one".
>
> This does require returning the exception_ptr by value. Is that what
> you're really objecting to? Or are you just too wedded to the
> semantics of variant? Like I said in the other thread, while I agree
> that the internal storage can be formed from a variant, logically the
> behaviour should not be that of a variant. Otherwise you'd just use a
> variant.
If you consider that one of the valid states of outcome is an
exception_ptr without a exception, and outcome::exception() returns the
transported exception_ptr or an exception_ptr without exception the user
is unable to know exactly what was reported. Maybe you don't need more,
but I believe we need to have an access to the transported failure with
a simple function.

Could you show me an example of use of exception() that make the code
simpler?
>
>>> No, each one returns an error_code. They do exactly the same thing
>>> in all cases. (Except perhaps expected, but that's because it's
>>> aiming to follow a standard that might mandate different behaviour.)
>> No, some returns the stored error code and others can calculate one
>> that doesn't correspond to the error that was transported.
>> Not all the functions that return an error_code must be named equal.
>> A function must convey the intent.
>
> They return an error_code that represents the state of the object.
> This is either the actual transported error_code, or an error_code
> that means "success", "an exception occurred", or "no value present".
> This does not seem weird in any way to me.
>
> Note that expected<T,E> can't do that, because it doesn't know how to
> construct an E that represents anything; it can only return what was
> transported or fail (either by UB or throw). Thus expected<T,E> is
> forced to just be a plain variant with narrow interface. This makes
> it a good building block but a bad end-user type. This is presumably
> what motivated Niall to write result<T> and outcome<T> in the first
> place; I believe he's even said so.

So if expected, that is class close to outcome and result, uses error to
access the transported error, outcome and result should use another name
to return an error_code that represents the state of the object.
We can as well change expected::error to use another name.
Why do you want to use the same name for different things?

Vicente


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk