Boost logo

Boost :

Subject: Re: [boost] [review] Review of Outcome (starts Fri-19-May)
From: Robert Ramey (ramey_at_[hidden])
Date: 2017-05-27 00:22:35


On 5/19/17 6:43 AM, charleyb123 . via Boost wrote:
> Hi Everyone,
>
> The formal review of Niall Douglas' Outcome library starts today
> (Fri-19-May) and continues until Sun-28-May.

I was sort of astounded at the volume of comment this submission
received. I can't go through it all. I can barely skim it. Most of
this went into implementation details which I wasn't prepared to invest
the time to understand. But it piqued my interest, so I resolved to
write a review from the stand point of a naive user such as myself who
might want to improve his approach to error handling.

I confess embarked on this task with a certain dread. I had previously
gone through Naill's documenation of AFIO and found it completely
unintelligible. Right away, I'll say that this is much, much better.

> Outcome is a header-only C++14 library providing expressive and type-safe
> ultra-lightweight error handling, suitable for low-latency code bases.

LOL - This is from the doc - and right away is off putting. It includes
a lot which may not be true. I would have preferred something like:

"Output is a form of variant. It can hold either some value type or
some type indicating an error result. Usage of Output will ease the
coding and usage of functions which might return errors."

> Key features:
>
> *- Makes using std::error_code from C++11's <system_error> more convenient
> and safe

The documentation provides useful explanatory information which isn't
really available anywhere else in a convenient manner. But lots of
system_error aren't addressed - like a key part - error_condition.

It's not reallypart of outcome even though outcome supports it. It's a
separate subject. I think almost all reference to it should be dropped
from outcome. Useful information regarding error_code should be
completed and added to boost::system_error via a PR to the boost.system
library documentation.

> *- Provides high-quality implementation of proposed std::expected<T,E> (on
> C++20 standards track)

Personally, I find it distracting when authors describe their own work
as high quality, or other laudatory phraseology. Spend the effort
demonstrating the problems that it solves for me other rather than how
great you believe it is.

> *- Good focus on low-latency (with tests and benchmarks)

The benchmarks seem to show that outcome is similar in performance to
returning an integer or error_code. This is what I would expect.

The benchmarks also purport to show that exception handling is much more
expensive than returning an integer or error_code. Well we know that if
the exception is thrown. But what if it isn't as is usual? What about
the cost of checking the error at each level? This isn't addressed
which is not surprise as it would be difficult to make a convincing
benchmark which shows these numbers. Since the timings for exceptions
provide no useful information and the other ones are all the same, I
would just drop the whole section and replace it with a sentence
"outcome provides similar efficiency as returning a returning any other
error indicator."

> *- Error-handling algorithmic composition with-or-without C++ exceptions
> enabled

I would drop just about all the references to exceptions. It's an
orthogonal issue. If you've decided you need exceptions or that they
are convenient in your context, you're not going to change to outcome.
If you've already decided on returning error indicators (for whatever
reason), you're not going to switch to exceptions. You might want to
switch to outcome. So comparison with exceptions isn't really relevant
at all.

Pretty much the same for error codes. You might or might not use them -
but this is a totally orthogonal question as to whether you use outcome
or not as it is (I think) designed to handle any type as an error return
types.

Eliminating all the irrelevant material would make the package much,
much easier to evaluate and use.

FWIW - I touched upon some of these points in

https://www.youtube.com/watch?v=ACeNgqBKL7E

> *- No dependencies (not even on Boost)

Yowwww. This terrible. I looked a little through boost-lite.
config.hpp - repeats a lot of stuff from boost/config. Doesn't seem to
provide for compilers other than gcc, clang and some flavors of msvc.
Looks like some stuff might have some value - but with not docs/comments
it's pretty much useless if something goes wrong - like a compiler upgrade.

chrono.hpp - what is this doing in here?

precompiling <tuple> etc..... Looks like you're trying to solve some
other problem here -

goes on an on.

The questions of pre-compiled headers/modules/C++11 subset of boost have
nothing to do with outcome and shouldn't be included here. Focus on one
and only one thing: The boost.outcome library. It's quite a small
library and trying to make it carry the freight for all these other
ideas will deprive it of the value it might have.

> This review is timely, as C++17 brings us std::optional<T>. The upcoming
> std::expected<T,E> (an implementation provided in Outcome) is a
> generalization of std::optional<T> that provides a <success|fail> value,
> where the unhappy result is a 'std::error_code' or an instance of
> "your-chosen-error-type".
>
> The library further provides 'outcome<T,error-code,exception-ptr>' for
> handling <success|error|exception> to safely wrap throwing APIs.

I didn't understand this either before or after reading the
documentation. I should confess that I don't understand the value of
exception_ptr, nested_exceptions, etc. either. Another good reason for
excluding discussion of exceptions and probably some code from the
library documentation and code.

> Documentation:
> https://ned14.github.io/boost.outcome/index.html

The documentation is built with Doxygen - which, though convenient,
works against producing helpful readable documentation.

> Note: Tarball might be easiest; but if you want to clone from GitHub
> directly, after the clone you should run the following command to get the
> source zip exactly: git submodule update --init --recursive

Another bad idea. Git is complex enough without adding submodules at
the library level. I would have to spend more time than I want to
uderstand what good this is doing for us. It's likely this would go
away of the scope of the library were limited to what it should be: outcome.

> Please post your comments and review to the boost mailing list
> (preferably), or privately to the Review Manager (to me ;-). Here are some
> questions you might want to answer in your review:
>
> - What is your evaluation of the design?
LOL - it's hard to discern the design from the docs and code. But
there's not many ways to implemented. The questions I would have would
be about never empty states and stuff like that but I don't see any of
that in the docs.

Another very weird thing. In spite of the size of the documents, there
is not reference page for outcome<T, E> which tells me what ... OK
looked harder and I did find it buried under Classes/Class
List/outcome/v1_xxx/outcome. In here I find a I have some questions:

typedef policy::monad_policy_base< policy::basic_monad_storage<
policy::monad_policy< R > >, R, error_code_extended, std::exception_ptr
> ::implementation_type implementation_type

What the heck is this - and why would I need to know it? It's marked
public which suggests that as a user I might need to access it - but the
documentation does tell me what it does or why I would like to mess with it.

OK - now I turn to the class definition of outcome. I see a boatload of
constructors. Hmmm I had thought that outcome was a template of the form:

boost::outcome<T, E> indicating that it returns a type T or E.

But no where do I see this. Perhaps I'm wrong about outcome being a
template.

I expect to see type requirements (aka concepts) for T and E. Can any
types be substituted? It's a mystery. Hmmm maybe initial example
and/or looking at the source code will be useful. First of all, the
example has no #include <output.hpp> or some such. I go looking for
that and .... I can't find it. It's somewhere hidden through a maze of
... preprocessing?

> - What is your evaluation of the implementation?

Needs to be slimmed down and all the extraneous stuff removed. This
isn't a vehicle for upending boost. This is trying to get as modest
simple header only library into boost in a way which makes it useful to
users nothing more.
>
> - What is your evaluation of the documentation?
A lot better then I expected!

> - What is your evaluation of the potential usefulness of the library?
I think it would be useful. It's basically a specialization of variant.
  As such it doesn't bring much we don't already have. BUT, a good
explanation and simple usage would promote a good and consistent way of
addressing returned error codes ans so might be useful
>
> - Did you try to use the library? With what compiler? Did you have any
> problems?

I only read the documentation and perused the source code.

>
> - How much effort did you put into your evaluation? A glance? A quick
> reading? In-depth study?

A couple of hours reading the docs and source code.

> - Are you knowledgeable about the problem domain?

somewhat. I understand error_code and have implement a version of this
facility to address a need in my own code.
>
> And most importantly:
>
> - Do you think the library should be accepted as a Boost library?

No

There is a temptation to say Yes - with conditions. I would hope that
this temptation is resisted. It should just be rejected. There is no
way that this library can be fixed without separating out huge parts of it:

preprocessing all the headers - it's very hard to follow this, it even
looks like it includes parts of the standard library as well as boost.
What if every library did this?

error_code looks intertwined at least in the docs.

The class versioning scheme is interesting. I could live with it but
I'm not convinced that boost should adopt it. And I'm less convinced
that just one library - especially one that looks so simple as outcome.

If one thinks I'm being two harsh, I propose the following experiment:
Take a pair of left over interns from C++Now and direct them to the
outcome git hub package. Give them a simple task to use the outcome
library. See if they can complete it in one hour or less.

On the other hand, I believe it wouldn't be too tough to take the pieces
of this library already made and compose them into a more tightly
focused package that would fit into boost in a natural way that users
would useful and easy to use. But the look and feel of the package
would be entirely different than the current one so It would really have
to be reviewed again. I also believe that if the package were
re-formulated as I suggest, the review process would be much, much less
onerous for everyone involved.

FWIW - my views on what a library has to have are summarized here:
https://www.youtube.com/watch?v=ACeNgqBKL7E


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