Boost logo

Boost :

Subject: Re: [boost] Outcome v2 is feature complete
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-07-11 14:33:48


2017-07-11 16:09 GMT+02:00 Niall Douglas via Boost <boost_at_[hidden]>:

> >> Github: https://github.com/ned14/outcome
> >>
> >> Docs (highly incomplete): https://ned14.github.io/outcome/
> >
> > This is a welcome development, if still a bit too-policy-heavy for my
> > taste.
>
> The policy is there purely to say what to do when you call .value() on a
> non-valued result/outcome. Compelling use cases could be:
>
> - Delay constructing metadata to accompany any exception throws.
> - Use .error() as a key to look up an associative map.
> - Invoke Emil's Noexcept instead :)
>
> One could have used ADL customisation points instead, but those tend to
> surprise end users, so I felt the policy approach was better and leave
> the ADL points for advanced users.
>
> > Regarding construction, I have the following suggestion.
> >
> > Instead of reusing in_place_type, use the following tag types:
> >
> > struct in_place_value_t
> > {
> > constexpr in_place_value_t() noexcept {}
> > };
> >
> > constexpr in_place_value_t in_place_value;
> >
> > struct in_place_error_t
> > {
> > constexpr in_place_error_t() noexcept {}
> > };
> >
> > constexpr in_place_error_t in_place_error;
> >
> > This is isomorphic but superior to using in_place_index<0> and
> > in_place_index<1>, which were my initial choice. Now the ambiguity when
> > T == EC is resolved and
> >
> > template<class... A> result( in_place_value_t, A&&... a );
> >
> > always initializes the value and does not need to be disabled when T ==
> EC.
>
> You've struck on a very interesting design point indeed, and one which
> caused me a few restless nights.
>
> My initial approach was to do exactly what you just described, after all
> it's how v1 implemented its tagged emplacing constructors, and v1 had a
> host of make_(valued|errored|excepted)_*() functions too. v1's API was
> all about construction of specific, named state.
>
> But upon further reflection - and I want to emphasise that I think the
> jury is still out on this decision, and I may revert during the next few
> months - I decided that std::variant<> by-type emplacement was superior,
> so I went with that instead.
>
> The reason why is subtle. These types, result & outcome, are not in the
> same use space as Expected where expected<int, int> is perfectly
> reasonable due to its use case as a monad. result & outcome are
> specifically intended to be used for one thing and one thing only:
> returning value *or* error from functions.
>
> And for that one use case, result<int, int> makes no sense. Actually,
> it's stronger than that: result<int, int> must **NOT** make sense.
> Everything about the API and contract must loudly proclaim that
> "ambiguous success and failure types are discouraged" because the type
> system should be set up to reflect the non-ambiguity of success vs failure.
>

maybe result<int, int> indeed looks suspicious, but using
result<error_code, error_code> might come as quite natural:

auto select_first_error(vector<error_code> v)
  -> result<error_code>
{
  if (!v.empty())
    return result<error_code>{in_place_index<0>, v[0]};
  else
    return result<error_code>{in_place_index<1>, MyErrc::NoFirstElement};
}

Regards,
&rzej;


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