C++ Logo

std-proposals

Advanced search

Re: P2192R0 -- Transparent return type

From: Dusan Jovanovic (DBJ) <"Dusan>
Date: Wed, 29 Jul 2020 13:22:05 +0200
Hi Jason,

many thanks for the comprehensive critique on style especially. It will
shape the R1 certainly.

now ... the core idea is "metastate" .. it has nothing to do with C++20
"concept"'s. function return metastate is not a state .. state would be
"error or no error". one can capture it, but metastate is not a type.
perhaps I should use the synonym: "theory" or simply "core idea".

Technically std::valstat" is just a meta state carrier (an vehicle).
Implemented in C++ using C++ std lib.
It is equally valid (aka "core idea compliant") to implement it in C ... as
the article shows. Or any other imperative language. One can imagine the
whole new CRT implementation using the valstat core idea.

What do you think on other benefits of using metastate (aka valstat) like
"no exceptions"? I might say "no exceptions for free". What do you think of
the attempt to conform to the P2000R1 recommendations?

lastly comparing valstat metastate idea to std::expected or outcome is
"comparing apples and pears" .. I should clearly explain it in the doc.

Again many thanks on the time spent and on really valuable comments.

On Wed, 29 Jul 2020 at 02:32, Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> == Editorial comments ==
>
> The biggest editorial issue is stylistic. All of your paragraphs are
> numbered, but they read like numbered lists. If your intent is to
> number your paragraphs (and it's *really* unnecessary and distracting
> to read through), then you need to reformat the PDF to make the
> numbering look less like numbered lists (the way the standard does).
>
> Coupled with this is the fact that you often just write one sentence
> on a line, which makes it look a *lot* like you're writing a numbered
> list, rather than actual paragraphs that can be referenced. This is
> really distracting to read. The very first two items in the Abstract
> are just two sentences of the same paragraph. It reads so much better
> as a full paragraph:
>
> > This is a proposal to introduce a behavioral pattern for optional but
> standard function return handling. It is an extremely simple type and
> behavior pattern of both producers and consumers using it.
>
> The second sentence is simply providing details for the first. They
> belong in the same paragraph, not on different numbered lines.
>
> Lastly, if you're going to write in a style so reminiscent of a formal
> standard, you should be a lot less conversational in the tone of your
> writing (such as the use of epigraphs starting certain chapters).
>
> On other points, you frequently use the word "concept" for the thing
> you're defining. You use this word a *lot* long before we actually see
> `valstat`. Coupled with how you often maligned other similar
> value-and/or-error types for being too large, that gave me the
> impression that you were about to unveil an actual C++20 "concept" for
> such a type. You should make it much more clear that you're talking
> about an actual type, not a C++20 constraint.
>
> Also, you use the phrase "returns concept" or "returns handling
> concept" in multiple places. This is... confusingly worded. I did some
> Googling, but I couldn't find reference to such phrases anywhere else,
> so it does not appear to be a term of art. My understanding is that
> these terms broadly represent the general concept of a "value and/or
> error/status" type. If that's the case, you need to make that much
> more clear up front, or to use terminology that is more clear and
> obvious to the reader.
>
> You talk about how "meta-states" are an implied idea of `valstat`,
> rather than being a part of its definition in any material way. That's
> fine... except that right before that, you showed a large code-block
> for the `state_combination` function that looks remarkably like C++
> that outlines what those concepts would look like. If it is not your
> intent for "meta-states" to be a formal part of C++ code, then you
> shouldn't have a code block talking about them.
>
> === Some typos ===
>
> * "adoption of this proposal should contributes, to all the three
> 'requirements' above." You mean "should *contribute*", singular.
>
> * "Thus it can not solve the issue of constructors not throwing
> extensions but needing to report the outcome." I think you meant
> "exceptions", not "extensions".
>
> == Technical Discussion ==
>
> The question that was most on my mind after seeing what `valstat` was
> after page-after-page of build-up was... ***why?*** And this question
> has many parts.
>
> === Why Not The Alternatives? ===
>
> I find your arguments for why one should not use `std::expected` (part
> of Library Fundamentals v3) or `boost::outcome` to be... odd. Now, you
> do mention a viable technical reason, but I'll get to that in a
> minute. The less viable technical reason you bring up is that the
> implementation of these types is long, which is bad.
>
> But... why is that bad?
>
> I'm not being asked to *write* `expected`, just as I'm not being asked
> to write `std::vector` or `std::optional`. That doesn't mean that they
> can have whatever bloated interfaces we want, but I don't find lines
> of code to be a convincing reason not to use someone's type.
> Especially in a post-Modules world where the compile time of such code
> is a lot less relevant.
>
> If you were defining an actual C++20 `concept`, then I could
> understand having a discussion about what interfaces should be
> required by users implementing said concepts vs. which interfaces
> aren't worth the cost. But there is no C++20 concept; there's just
> `valstat`. I don't see a reason to care that `valstat` is short while
> `expected` is long; what I care about is how *functional* these types
> are.
>
> Also, it should be noted that many C++20 features will likely make
> `expected` and `outcome` *much* shorter, and even easier to code. Many
> of the tricks they have to do are workarounds for not having
> constraints on non-template constructors, or dealing with `explicit`
> constructors of various types. C++20 makes those much, much easier to
> deal with.
>
> === Why One Size Fits All? ===
>
> You make an interesting point with your "meta-state" idea. You make
> the point that it is conceptually valid, in a general sense, for a
> function to return any pairing of value and "error". Each combination
> of these possibilities has a distinct meaning.
>
> However, one thing that `valstat` doesn't take into account is... do
> all functions actually provide all meta-states? If I'm writing a
> function that either returns a value or an error, and I return
> `valstat`, that heavily implies that the user should check all four
> possibilities.
>
> Wouldn't it make a lot more sense for such functions to return a type
> that is *explicitly* restricted to being just Value or Error? That's
> good self-documenting code practice, yes?
>
> Indeed, if a function returns neither Value nor Error, in your
> meta-state diagram, that constitutes "empty". But again, is that a
> state that *every function* that returns an Error could possibly
> yield? It's right there in `valstat`'s API, so if I receive one from a
> function, that ought to tell me that I should assume it is a
> possibility.
>
> Basically, my point here is that there are a variety of Value+Error
> circumstances, and having a single type to handle them all is
> basically making your API lie to the user.
>
> To me, the only advantage of this one-size-fits-all solution is that
> you get to use structured binding to store and unpack the results
> (ValueOrError types can't use structured binding). But even that is
> pretty flimsy, all things considered; it just makes it slightly easier
> to access the values.
>
> === Why Status is Error? ===
>
> This is another design aspect I find odd. If there's a Value in the
> `valstat`, then the status code is a Status value. If there is no
> Value, then the status code is an Error.
>
> To me, these represent two very distinct pieces of information. Yet
> they are required to have the *same type*. Why?
>
> If I want to transmit a status code with a value to represent
> completion with added information, then I ought to put the status code
> inside of the value. That is, my Value type would be a
> `pair<RealValue, StatusCode>` or similar. This gives me the
> flexibility to make the status code a different type. Error types
> might have significant information in them, but status codes don't
> have to. Or if they do, it's very *different* information.
>
> === Why So Minimal? ===
>
> A good lingua franca type, in my mind, has two very important properties:
>
> 1. It provides a common type for frequently-used idioms. This permits
> interoperation between code, as they both use the same type for the
> same kind of thing.
>
> 2. It provides useful functionality in and of itself.
>
> Even if `valstat` solves #1, it fails at #2. The type provides
> *absolutely nothing* in terms of actual functionality in and of
> itself.
>
> To understand what I mean, consider the case of returning an error. In
> your type, you have to say `return {{}, ErrorValue};`. For a user to
> understand that, they have to remember an idiom.
>
> For `expected`, it looks like this: `return unexpected(ErrorValue);`.
> This is so readable that you can literally read it aloud and
> understand what it means: "return an unexpected error value". There
> are no spurious braces or other unnecessary noise. It says exactly
> what it does; no idiom needs to be remembered.
>
> Basically, those thousands of lines of code in `expected` and
> `outcome` have a *purpose* to them. They're not noise.
>
> We don't write lingua franca types just for interoperability. We write
> them because they're *useful* types. `string_view` provides useful
> utility in and of itself outside of interoperability. You can
> `string_view` a string literal, thus preserving its length and not
> allocating storage, for example. And you still get a lot of
> `basic_string`'s string manipulation API.
>
> If we were following your paradigm, `string_view` would just be a
> struct with a public `const CharT *` and a public `size_t`. No special
> constructors, no range members, no string manipulation members, just a
> pointer and a size. It would only ever see use as an interoperability
> type, not as an actual, functional type.
>
> Even ignoring everything else I said about `valstat`, if you feel that
> the overall design of two optionals is good, there needs to be *more*
> to the API than just a pair of optionals. Why not provide a
> visitation-like mechanism that makes the four meta-states something
> real rather than an idea? That'd probably work great with various
> inspection proposals in the works.
>
> Why not develop an actual *C++ concept* for these kinds of things?
> That way, the pointer version you suggest at the end of your paper can
> work with code that uses the concept too.
>
> If I'm writing code by myself, I would never use `valstat`. It
> provides nothing that I couldn't do myself almost as easily.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2020-07-29 06:25:44