Boost logo

Boost :

Subject: Re: [boost] [outcome] High level summary of review feedback accepted so far
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2017-05-27 16:26:50


>> and I've retained the current wide result<T> and outcome<T>, just
>> renamed to checked_result<T> and checked_outcome<T>.
>
> This isn't enough to compensate. One, checked_result is so much longer
> to type that this is obviously an encouragement to use unchecked by
> default, a questionable guideline.

I would agree. But well, we were outvoted. And that probably means
rejection of this library, as the presented library does not implement
what the majority want (yet).

I would say that I expect absolutely nobody to actually type
checked_optional_result<T> each and every time. What they'll do is:

namespace mylib
{
  template<class T> using result = checked_optional_result<T>;
  ...
}

Indeed AFIO already typedefs Outcome's result into its namespace to
prevent me typing outcome::result all the time, which is annoying.

Also remember that checked_optional_result<T> will itself be typedefed to:

template<class T> using checked_optional_result = outcome_impl<T,
error_code_extended, void, default_to::empty, emptiness::formal,
observers::wide>;

> Two, since result/outcome are used on
> API boundaries, one can't just declare "we'll use checked_result"
> because the API you're calling isn't yours and therefore the choice of
> the return type isn't yours either. If it returns the unchecked result,
> that's what you'll receive. And it will return the unchecked result.

It's only unchecked on access, so if you store the return type from the
API into a checked variety (remember we allow implicit conversion from
unchecked to checked, but not the other way round), then the caller gets
to choose whether access is checked or not.

> This breaks the idiomatic use of
>
> result<T> function() noexcept;
>
> U function2() /*yesexcept*/
> {
> auto r = function().value(); // want throw on error
> // ...
> }
>
> where you're transitioning from a noexcept lower layer that signals
> errors with error_code, to a higher layer that signals errors with
> system_error.

Yes, but surely the programmer can see that function() returns a
result<T>, not a checked_result<T>. So if they want the throw on error:

    result<T> function() noexcept;

    U function2() /*yesexcept*/
    {
        auto r = checked_result<T>(function()).value(); // want throw on
error
        // ...
    }

A free function called "checked()" might be nice to convert some
unchecked input outcome into its equivalent checked variety. But that's
just sugar.

> Since this idiomatic use is one definitive aspect of the error handling
> philosophy that result/outcome represent, going to unchecked would be
> detrimental.

I agree. But we were outvoted.

> If value/error must be unchecked, then provide checked overloads,
> instead of a different type. Better yet, keep value/error checked,
> provide unchecked overloads.

The reason I chose different types is because we can have the unchecked
varieties call __builtin_unreachable() so static analysers trap stupid
usage of the unchecked objects.

Now, that doesn't rule out additional member functions. A decision needs
to be made on whether to use type converting free functions like "U
&&checked(T &&)" or whether to go with duplication of checked member
functions instead. So, should the type system represent the checked vs
unchecked semantics, or should it not?

I don't know yet. I am still thinking about that design choice. It's why
I hadn't mentioned it. But I am on to the problem.

> This is another instance where I now see the wisdom in providing a
> "richer" (another man's cluttered) interface that is, in my opinion,
> inelegant and unnecessary; in this case operator*. Give people who want
> unchecked what they want, have plausible argument for calling it op*
> instead of value ("consistency with optional").
>
> A bit daft if you ask me but what can one do, we were outvoted, after
> all, and democracy is where it's at.

I hate with a passion that choice of giving operator*() unchecked
semantics. Why should it be operator*()? Why not .value_raw(), or
.value_unsafe()? The latter two would be far superior, not least that if
you really want narrow semantics and less safety, the programmer has
type more in exchange.

But as you say, democracy.

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

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