Boost logo

Boost :

Subject: Re: [boost] [outcome] Ternary logic -- need an example
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-05-19 07:01:02


2017-05-19 0:49 GMT+02:00 Niall Douglas via Boost <boost_at_[hidden]>:

> > My personal preference on this is that if you call `o.error()` before you
> > have confirmed you actually have an error, you are doing something
> wrong. I
> > would classify such situation as undefined behavior. But your choice fits
> > into the scope of undefined behaviour: if program can do anything, it
> might
> > as well return a default-constructed error_code.
>
> Ah but remember that outcome<T> and result<T> both guarantee that E will
> be a type meeting the std::error_code contract.
>

Ok, I remember, both error_code and exception_ptr are
default-constructible. It is not immediately clear to me that a
default-constructed error_code represents a no-error condition. In the blog
post by Chris Kohlhoff you refer to, he uses the following enum for
representing http error conditions:

```
enum class http_error
{
  continue_request = 100,
  switching_protocols = 101,
  ok = 200,
  ...
  gateway_timeout = 504,
  version_not_supported = 505
};
```
Which implies that numeric value 200 means no-error and a value initialized
`http_error` is meaningless. Maybe this does not affect the value of a
default-constructed `std::error_code`, but surely it adds to the confusion.

> Therefore calling o.error() on a valued outcome returning a default
> constructed (null) error code is exactly correct: we return there is no
> error.

Only under some definition of "correct". By correct, you probably mean "not
invoking UB" and "being compliant with your specification", but it is not
intuitive at all that a function should return this or that when invoked in
a context that is most likely a programmer's bug.

> No undefined behaviour needed, and you can write your code using
> Outcome with the hard assumption that o.error() will always return an
> accurate view of the current state. No need to check .has_error(), or
> anything like it.
>

Modulo this situation with `http_error::ok == 200`. But with this you are
also saying, the library provides two ways for checking if you have an
error:

o.has_error(); // option 1
o.error() == std::error_code{}; // option 2

And by describing clear semantics for option 2, you are saying, it is
equally fine to use option 2 for checking if we have an error. This
encourages the usage of option 2, but I would not want my colleague
programmers to start using this syntax, because I then cannot tell proper
usages from inadvertent omissions. And reading the value of `error()`
without having confirmed that some error occurred is almost surely a bug,
even if you can assign a well defined semantics in `boost::outcome` for it.

> > On the other hand, you do loose something when you chose something else
> > than undefined behavior: the potential for such user bugs to be detected
> by
> > static analyzers.
>
> No static analyser that I am aware of can diagnose when someone is
> causing UB in std::optional<T>. Writing one which didn't spew false
> positives would be hard, even if the entire program were visible. After
> all, maybe the user is _intentionally_ doing the reinterpret_cast by
> effectively treating the common state as a union?
>

Challenge accepted.

>
> Remember, expected<std::error_code, std::error_code> is legal, though
> Outcome's Expected doesn't allow it.
>
> > But I do not understand why in my program I would want to write:
> >
> > ```
> > if (o == tribool::unknown) {}
> > ```
> >
> > How is that better ina any way from `o.is_empty()`?
>
> I don't want to talk about this much as it's outside the scope of the
> current review, but in functional programming using Outcome the tribool
> can help make the logic much clearer. A common pattern I've used
> personally is for true and false to select which branch of lambdas to
> call next, and empty means to terminate processing immediately with no
> further functions called.
>
> I should emphasise that those extensions are disabled in the presented
> library, and are out of scope for this review.
>

I am fine with this scope disabled. However, `tribool` is still in scope,
so I consider it valid to ask about its usefulness (especially given that
all contexts in which it would prove useful is disabled). Maybe `tribool`
should be also removed from the scope?

Regards,
&rzej;


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