Boost logo

Boost :

Subject: Re: [boost] [gsoc-2013] Boost.Expected
From: Pierre T. (ptalbot_at_[hidden])
Date: 2013-04-28 12:27:49


On 04/28/2013 05:20 PM, Vicente J. Botet Escriba wrote:
> Le 28/04/13 16:22, Pierre T. a écrit :
>> On 04/27/2013 03:30 PM, Vicente J. Botet Escriba wrote:
>>> Le 27/04/13 09:39, Pierre T. a écrit :
>>>> On 04/26/2013 08:17 PM, Vicente J. Botet Escriba wrote:
>>>>> Le 26/04/13 14:22, Pierre T. a écrit :
>>>> I like this proposition but at first, it seems a bit "overkill". I
>>>> would like to have more opinions about it. Through it seems to be
>>>> less compatible with the "visitor" idea. The visitor resolves on
>>>> types and most of the error code have a same type. So it would
>>>> visit only one type.
>>> Right. But this independent on whether you use a wrapper exception
>>> or not, isn't it?
>> Yes, it gives a good point to the traits.
>>>>
>>>>> * Default Constructor or constructor from nullexpect
>>>>> What is the advantage of having a expected instance that doesn't have
>>>>> neither a value nor an exception?
>>>>> How would the user manages with this possibility?
>>>>> Are you looking to make expect movable?
>>>>>
>>>> Basically, I noticed that classes without default constructor (or
>>>> default state) are burdensome to use. Indeed, you cannot store an
>>>> expected in a class as a member if not initialized in the
>>>> constructor. Or doing something like:
>>>>
>>>> expected<int> e;
>>>> if(…)
>>>> else(…)
>>>> return e;
>>>>
>>>> Through, I removed the default constructor because I found it
>>>> unclear. I use a nullexcept because it was a good idea in
>>>> Boost.Optional with nullopt.
>>> Yes it was one. But the definition of optional is there to allow a
>>> state that means value not present. expected<> is designed to have a
>>> value or an exception.
>>> Could you answer to the question
>>> How would the user manages with this possibility?
>> By testing == nullexcept (operator== not in the proposal, sorry),
>> however you are right, expected must contains an exception or a
>> value. On the other hand, it's nice to provide a default constructor,
>> so an idea could be to add a method "unitialized_error()" in the
>> trait class.
> Why do you need a default constructor.
Because it can be useful for the following situation:

expected<T> func(bool b)
{
   expected<T> e;
   if(b)
   {
     e = f();
     // use e
   }
   else
   {
     e = g();
     // use e
   }
   return e;
}

Some programmers prefer to have only one return statement instead of having:

expected<T> func(bool b)
{
   if(b)
   {
     expected<T> e = f();
     // use e
     return e;
   }
   else
   {
     expected<T> e = g();
     // use e
     return e;
   }
}

>>>>
>>>> Finally, I'm not sure to understand how it's related to the movable
>>>> ability of Boost.Expected.
>>> I let you try to define move move semantics for expected and you
>>> will see why this is related.
> After thinking more on this the moved object could present its
> exception_ptr if it has one, so move semantics doesn't force to have
> an uninitialized expected<>
Not sure to get it,

expected(expected&& e) // nothing in the initializer list because we
don't know if we must move T or Error.
{
   if(has_value)
     // move value
   else
    // move error
}

In case T or Error haven't a default constructor, the previous code
can't work, right ?
Could we add a dummy field in the union such as:

union{ T value, Error error, bool internal_uninitialized};

But I'm not sure it's what you meant.
>>>>
>>>>> * then/otherwise issues
>>>>>
>>>>>
>>>>> But the design error is not on the otherwise function but on the then
>>>>> function.
>>>>>
>>>>> Resuming, I'm not more for the 'then' function.
>>>>>
>>>>
>>>> I'm totally agree with you. But I think that I misnamed the
>>>> "otherwise" method. I think the "then" method is really useful,
>>>> it's like an automaton. With e.then().then().then(), the treatment
>>>> is stopped anytime when an error occurs in a then method.
>>> How would this be stopped? throwing and exception?
>> It's not stopped but the "then" method launch the function only if
>> expected contains a value.
> And what return if it not contains a value?
>>>> Also, I think that the function taken by then should return void
>>> And which value would you pass to the second then call?
>> The same.
> Sorry I don't follow. What The do you mean by the same?
>>>> or an expected.
>>>
>>> Maybe. But if we don't know how it is stopped chaining them has no
>>> sens. When the call to the function is asynchronous it is easy to
>>> stop it but synchronously it implies an exception which is against
>>> the goal of expected.
>>>> In fact the otherwise method is the error visitor. A common usage
>>>> would be to chain it in the end:
>>>>
>>>> e.then(...).then(...).then(...).visit_error(error_visitor);
>>>>
>>>> visit_error is called if any "then" return an error. I found it
>>>> wonderfully useful.
>>> This could be possible; It would need that then() returns an
>>> expression template storing all the functions on the chain and only
>>> the call to the visit_error would evaluate the chain.
>>> I don't think it is worth proving this lazy evaluation.
>> To complete what I've said, the visit_error() would launch the
>> visitor only if the expected contains an error. No lazy evaluation is
>> needed.
> Ah I think I see now what was the initial intention. I suspect that
> you mean that as the result of e.then(f) would transport the exception
> stored on e if e is not valid, then the exception would be relayed
> until there is a call to visit_error and no call to any function will
> be done.
> Yes this is quite similar if we had exceptions and we had a try/catch
> block
>
> f0().then(f1).then(f2).then(f3).visit_error(error_visitor);
>
> try {
> f3(f2(f1(f0())));
> } catch(...) {
> error_visitor();
> }
>
> It would be great if we could have the equivalent for
> try {
> h(f(), g());
> } catch(...) {
> error_visitor();
> }
>
> when_all(f(), g()).then(h).visit_error(error_visitor);
>
> when_all could return a expected<tuple<T1, T2>> or something more
> specific so that then would extract the members of the tuple to call h
> with two parameters.
>
> This start to look more and more like the proposal for futures
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3558.pdf)
>
Sorry for the poor explanations about it, but you guessed correctly. I
read the futures proposal and the ideas in there are really interesting.
However because expected is not related to the time, I think we could
just do the following:

e.then(f, g).then(h).inspect(error_inspector)

where "then" takes an unlimited number of functions. In this case it
would return a tuple-wrapper class specific to the expected class (in
case users would like to store tuple inside expected).
About the "inspect" method, I try to find another name instead of
visit_error because it makes me think to the pattern visitor which is
too specific to hierarchy classes. I found that inspect was nice because
it's like in a factory where the chief "inspects" that everything has
been alright.

>> I'll try to send it before tomorrow. I will update it with the trait
>> classes and the others comments you made. I just have a question
>> about the proposal, should I use the Boost macro for noexcept,… or is
>> it better (for a proposal) to write it with the standard tools ?
> I use to document using c++11 without any reference to the emulations.
>
> Best,
> Vicente
>
Thanks,
Pierre T.


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