Hi Everyone, Some comments about `outcome`'s observers with narrow and wide contracts. First, the wide-contract functions. We have a situation when someone assumes that `o` currently stores an error_code, and with this assumption in mind calls `o.error()`, but somehow his assumption is incorrect. What do we want to do? 1. Try to report programmer's bug at run-time? 2. Since in this case it is a bug anyway and are screwed, at least let's make such behavior useful for other situations where `error()` is called conciously on `o` without an error code? The first scenario attempts at identifying a bug, bringing it to attention of developers (maybe in form of a log), and minimizing its consequences by perhaps skipping the execution of some part of the program in hope that the bug will no show symptoms again. The second scenario in a way ignores the bug, and lets the program continue, as though it was correct, and from now on the program is doing something else than what the programmer intended. But the semantics of the function are useful in itself. Users who check `o.has_error()` before calling it can use it as if `error()` had a single purpose and a narrow contract; and users who know the additional semantics can use it for other useful things, like saving themselves of writing some boilerplate code. It is my impression that some of these observer functions in `outcome` follow this second scenario: just do something useful for people who use it unchecked. Saving boilerplate is useful, but let's state it clear: these functions offer no protection against inadvertent mistakes: no run-time or static protection. If I call `o.error()` and it throws when `o.is_empty()`, it is a form of protection. If `o.error()` returns `E{}` when `o.has_value()`, this is no protection, it is convinience function that can be used as a narrow-contract observer. Maybe the interface of `outcome` should make this distinction and offer two sets: one for observers with run-time protection, and the other for nice convenience functions? Second, narrow-contract functions. Narrow functions acheive the following goals: 1. Simple design: function only does one thing, it does not have any if inside. 2. The interface of the library makes a clear distinction of what is valid and what is an invalid usage of the library. It helps in code reviews. For instance, you are looking for a bug in a big code, you find the following piece: ``` auto e = o.error(); ``` and it turns out that `e` contains a value. Did you just find a bug, or was it the author's intention to get an `E{}` at this point? You do not know with wide contracts. With narrow contracts you know: it is a bug. 3. Narrow contracts assist instrumentation in test builds and runs. If I have function `just_value` with a narrow contract, I leave myself the freedom: I can do whatever I want when has_value() is false. What can I do with this freedom: A. In test build, I can check the precondition at run-time and launch the debugger B. I can (run-time) check the precondition, and std::termiante() or throw an exception. C. I can ignore the potential precondition violation, check nothing and aim at some performance gain. D. I can put some code that assists the static analyser (like __builtin_unreachable()) E. I can put some instrumentation code that assists an UB sanitizer (like __builtin_unreachable()) F. I can put hints for optimizer (like __builtin_unreachable(), again) G. Finally, I can put a macro `PRECONDITION(has_value())` which can do any of the above based on the build configuration. Given that, the proposed function `value_if` (which returns a pointer to value) will not satisfy my needs. It has a wide contract, so I cannot put macro `PRECONDITION()` inside. It rturns a pointer. The pointer's operator-> inded does have a narrow contract, but it gives me no room where I can put macro `PRECONDITION()`. So I get some narrow contract, but without the benefits I am looking for. So that is my view of the observer functions. Now my proposal is to provide a pair of observers for each value, error_code and exception -- narrow and wide: `value()` and `just_value()`, `error()` and `just_error()`, `exception()` and `just_exception()` Or maybe, even three for narrow-contract, wide-contract with run-time protection, and for convenience function: `error()` -- wide-contract, runtime-checked precondition `just_error()` -- narrow contract `as_error()` -- convenience function, e.g. returns E{}, on value, monad::has_exception on exception, etc.. Regards, &rzej;