Thanks for your reply!
First of all, indeed adding `from_range` to the constructor makes sense.
Secondly, you seem to understand the proposal perfectly and your `when_all` example summarizes the same algorithm as what I've called `ranges::collect`.
However, `ranges::collect` has more features that I think are useful. For example, `ranges::collect` works with any potential_type (i.e. any type that works like `std::optional` or `std::expected`), can build any container and you can pass container arguments as arguments to the function (as I mentioned, this is really inspired by ranges::to).
You can read more about these capabilities in the README of my GitHub repos here.As for the function name itself, I'm not entirely convinced that `collect` is the most expressive, but on the other hand, I think `when_all` doesn't emphasize the container-building action performed by the function. But this is a secondary point that should be discussed later if discussions on the proposal go further.
Concerning `when_any`, it's true that it's more or less the opposite of `collect` or `when_all`, but it adds real value to add this function compared to a simple find.I think that `ranges::collect/when_all` or the equivalent constructor approach suggested by Barry is the most common way of handling a range of “potential types”: if an error occurs, we abandon the process and handle the error.
The other two possibilities, in my opinion, are:
* ignore errors and collect valid values (and this is made easy by `p3168r1: range support for optional` as seen in the first example shown in the paper)
* separate errors and valid values and process them separately. This can already be done easily with std::partition.
I agree with you that the constructor used in the ranges::to approach suggested by Barry can be confusing for the user and I tend to prefer the “when_all/collect” free function approach I suggested in the first place for the time being because it is more explicit for users.However, to be fair, I looked into the rust `collect` function to understand how it works on optional or expected and it is indeed done using a “constructor” (actually an implementation of the FromIterator trait) implemented in their equivalent expected/optional types.
So `collect` in Rust works exactly like std::ranges::to and the “when_all” algorithm I want to add is done in Rust in the way Barry suggests.
In Rust, this is common usage and anyone who calls collect on a `range<optional<value>>` to create an `optional<container<value>` knows that the result construction will follow the `when_all` behavior. Perhaps this would be less obvious to cpp developers, as it's not a language feature from scratch as it is in Rust.
About `std::ranges::to<std::optional>()` this CTAD behavior might simply be unallowed I think (i.e. no CTAD for this optional/expected constructor) so I'm not sure that invalidates the constructor approach. If you fully specify the form of the output (e.g. `std::ranges::to<optional<vector<int>>>()`), the “when_all” behavior will occur. And because of the function's output type, it's impossible to expect the when_any behavior you mentioned..In any case, do you think either approach is worth proposing? I still think it's generally necessary when working with a “potential_type” range, and that it's missing from the current standard. If it's worthwhile, what should I do next, and would someone more experienced than me be so kind as to help me with this process?Thank you,Corentin AdamLe ven. 27 sept. 2024 à 23:06, Arthur O'Dwyer <arthur.j.odwyer@gmail.com> a écrit :On Thu, Sep 26, 2024 at 5:07 PM Corentin Adam via Std-Proposals <std-proposals@lists.isocpp.org> wrote:I hadn't thought of it that way, but I agree with you that it would work too.
If I understand correctly, you mean some range constructors like this?Presumably more like this. (The only diff is the addition of `from_range_t`.)IIUC, the proposal is to take a range of optionals and collapse it into an optional of range, with this signature and semantics (if not this exact name):std::optional<std::vector<T>> when_all(std::vector<std::optional<T>> v) {if (std::ranges::all_of(v, [](auto&& x) { return bool(x); })) {return v | std::views::transform([](auto&& o) { return *o; }) | std::ranges::to<std::vector<T>>();} else {return std::nullopt;}}The obvious next step after `when_all` is `when_any`:std::optional<T> when_any(std::vector<std::optional<T>> v) {auto it = std::ranges::find(v, true);return (it == v.end()) ? *it : std::nullopt;}Now, I don't think either of these is suitable for `ranges::to`, because then what would happen when I write `std::ranges::to<std::optional>()`? Just specifying the shape of the output isn't sufficient to tell me (as the code-writer or as the code-reader/reviewer) what algorithm I want (or expect) to be used to get the data into that shape. When the algorithm is non-obvious, and/or when there's more than one possible algorithm that could be used, the Standard Library should explicitly name the algorithm.Coincidentally, it was noted on the cpplang Slack just a day or two ago thatchar hello[] = "hello";auto s1 = hello | std::ranges::to<std::string>();auto s2 = hello | std::views::all | std::ranges::to<std::string>();assert(s1 != s2);which is kind of another case of "when there's multiple possible ways to do a transformation, it's confusing to give them all the same name."–ArthurOn Thu, Sep 26, 2024 at 5:07 PM Corentin Adam via Std-Proposals <std-proposals@lists.isocpp.org> wrote:I hadn't thought of it that way, but I agree with you that it would work too.
If I understand correctly, you mean some range constructors like this?I've searched through the current proposals and it seems to me that these constructors haven't been proposed yet (even in the optional range support proposal), have they?If they haven't yet been proposed, what do you think about drafting a proposal to include them?
Another question, why favor the constructor from ranges approach over the collect free function approach?--Le mer. 11 sept. 2024 à 19:47, Barry Revzin <barry.revzin@gmail.com> a écrit :On Wed, Sep 11, 2024, 11:56 AM Corentin Adam via Std-Proposals <std-proposals@lists.isocpp.org> wrote:I've recently been working on a cpp implementation of the rust collect function: : https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect.This function has two main uses in rust:* construct containers from ranges (exactly the work achieved by std::ranges::to)* collect ranges of expected/optional values into expected/optional range of value. Thus, if all values in the range are correct (in other words has_value() == true), the return value is true and contains the range of underlying values. Otherwise, the return value contains the first error encountered in the range.ranges::to can support the second use-case as well. We just need to add the appropriate constructor to optional and expected.Barry
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals