Boost logo

Boost :

Subject: Re: [boost] [gsoc-2013] Boost.Expected
From: Pierre T. (ptalbot_at_[hidden])
Date: 2013-04-28 10:22:55


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 :
>>>> Hello,
>>>>
>>>> I wrote a proposal for the Boost.Expected project.
>>>> You can find a pdf version here:
>>>>
>>>> http://hyc.io/boost/boost-expected-proposal.pdf
>>>>
>>>> It's about the Boost.Expected project, I'll add my own informations by
>>>> the end of the week.
>>>>
>>>> Finally, I propose a single class design, and facilities for custom
>>>> error code. It's based on the Alexendrescu idea upgraded by Vicente J.
>>>> Botet Escriba and myself. I also took into account the Boost.Optional
>>>> class. If needed for this proposal, I can add documentation per method
>>>> (such as in official proposal), but I'm not sure it's useful without a
>>>> frozen interface.
>>>>
>>>> Please do not hesitate to comment it and request further
>>>> clarification,
>>>
>>> Hi,
>>>
>>> first of all, thanks for writing this proposal.
>>>
>>> I have some concerns
>>>
>>> * Single class:
>>> I'm all for a single class; but having an ExceptionalType template
>>> parameter which defaults to exception_ptr.
>>>
>>> template <typename ValueType, typename
>>> ExceptionalType=exception_ptr>
>>> class expected;
>>>
>>> We could define the traits that make the difference between having an
>>> exception_ptr and another error
>>>
>>>
>>>
>>>
>>> With these traits we are able to define the functions that have a
>>> specific behavior as e.g.
>>>
>>>
>>
>> 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.
>>
>> 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.
>>
>>> * then/otherwise issues
>>>
>>> Humm, I don't agree here with the proposed design (even if I made the
>>> then suggestion). What returns the then function if the instance has a
>>> value? I declared it as
>>>
>>> template <typename F>
>>> expected<typename boost::result_of<F(const expected&)>::type>
>>> then(F&& fuct)
>>>
>>> Let me name
>>> typedef typename boost::result_of<F(const expected&) RT;
>>>
>>> so the result would return the expected<RT>(fuct(value_)). Chaining
>>> this
>>> temporary with an otherwise call will work with the new temporary, and
>>> not with the original expected.
>>> I don't think this was what was expected ;-)
>>>
>>> E.g.
>>>
>>> string f(int);
>>> int f(string);
>>>
>>> expected<int> e= 1;
>>> e.then(f).otherwise(g);
>>>
>>> Here g will be called with expected<string>(fuct(value_)).except_. Hrrr
>>>
>>> expected<int> e= make_exceptional_expect(X);
>>> e.then(f).otherwise(g);
>>>
>>> Here g will be called with the exception_ptr stored in e. Hrrr
>>>
>>> That is g don't know if it is called with an exception stored on e
>>> or on
>>> the temporary resulting for the call to f.
>>>
>>> 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.
>> 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.
>> 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.
>>
>>> Using a visitor would as you proposed initially would be better.
>>>
>>> template <class V>
>>> void accept_visitor(V& visitor) const BOOST_NOEXCEPT
>>> {
>>> if (valid())
>>> visitor.visit(value_);
>>> else
>>> visitor.visit(expectional, except_);
>>> }
>>>
>> I'm not sure that we should use the visitor pattern for the value and
>> the error. Initially, I though at the visitor pattern because error
>> would be exception-based, so with some types to visit. And also to
>> provide a way to visit exception without throwing them with the get()
>> method.
> Well I let you think a little bit more about. The visitor is your
> idea. From my side I don't need it for the case where the Error is not
> an exception_ptr.
>>
>>
>> As usual, these discussions help a lot, but this time, I'm not sure
>> if our idea are still good ideas, that's why more opinions would help.
>>
> Yes, it always helps to know what others think about. Very often
> people has no time to, or would not take the time until you present a
> concrete proposal and implementation so they can play with.
> If you send your application, I'm sure that other mentors would
> comment on the subject you could request to them.
>
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 ?
> Best,
> Vicente
>
Thanks again for these comments,
Pierre T.


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