7

I am playing with C++23 std::optional additions, but I can not figure out how to elegantly access the value of object if optional is active.

I know I can use if, but that is so C++20.

I really like C++23 API changes, but I can not figure out how to skip the boilerplate of implementing identity. For example:

#include <functional>
#include <iostream>
#include <optional>

void print(std::optional<std::string>& name) {
    name.transform([](std::string& x) {
        std::cout << x << std::endl;
        // I just want to print, without modifying optional, but next line is required
        return x;
    });
}

int main() {
    std::optional<std::string> name{{"Bjarne"}};
    print(name);
}

It almost feels like std::optional is missing invoke member function.

note: in my example I do not chain anything else after transform, but that is for brevity, I care about optional not being modified.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 8
    Seems like you figured it out in the second sentence of your question. – Barry Feb 07 '22 at 21:59
  • @Barry I really do not understand why invoke or observe would not be useful, but ok... – NoSenseEtAl Feb 07 '22 at 22:01
  • 3
    I honestly think `if` is much easier to read and follow. – Galik Feb 07 '22 at 22:06
  • 1
    Your `transform` version uses 4 lines of code. My `if` version uses 2. – Galik Feb 07 '22 at 22:11
  • 2
    @Galik I don’t, because with `if` the compiler doesn’t check that you’re not accidentally using the value in the `else` branch. In other words, it’s utterly pointless. It would be less pointless if the language provided assistance here; e.g. if the compiler complained if we used `x` in the `else` branch in this code: `if(auto& x = *opt; opt) … else …;` But unfortunately the language isn’t designed that way. – Konrad Rudolph Feb 07 '22 at 22:15
  • The `and_then` member lets you run a function but doesn't modify the optional. If you ignore the return value of `and_then` doesn't it do what you want? https://en.cppreference.com/w/cpp/utility/optional/and_then – Jerry Jeremiah Feb 07 '22 at 23:04
  • @JerryJeremiah doesnt that need to return std::optional? The return type (see below) must be a specialization of std::optional. Otherwise, the program is ill-formed. – NoSenseEtAl Feb 07 '22 at 23:10
  • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r6.html#and_then has an example of using `and_then` and directly comparing it to an equivalent `if` - if you ignore the return code of `stoi` in that example it does what you are after, right? – Jerry Jeremiah Feb 07 '22 at 23:12
  • @JerryJeremiah "and a function like std::stoi which returns a std::optional instead of throwing on failure." So I think I still can not have function that returns void – NoSenseEtAl Feb 07 '22 at 23:23
  • No, you are right. It worked in my imagination. All `and_then` does for you is to not modify the optional - which wasn't a problem for you because you were returning the same thing you received. You still need to return something. Sorry for not testing my throry before suggesting it. – Jerry Jeremiah Feb 08 '22 at 00:32
  • 1
    "... since that will prevent implicit moves" wait implicit moves from _what_? – Barry Feb 08 '22 at 02:31
  • Can you provide the construct in another (functional) programming language for comparison? – Sebastian Feb 08 '22 at 05:06
  • @Barry, nevermind, I was wrong I assumed it will suffer from the same problem accumulate had, where there would be unnecessary copy. now it is weird that it does not, but that is another question – NoSenseEtAl Feb 08 '22 at 07:40
  • @Barry mind :) , please see https://godbolt.org/z/bs3fxqh5z – NoSenseEtAl Feb 08 '22 at 07:50
  • 4
    `void` returning `transform` was [considered and rejected](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r6.html#mapping-functions-returning-void) – Caleth Feb 08 '22 at 09:55
  • Here is the vote result https://github.com/cplusplus/papers/issues/112#issuecomment-459286258 this topic could be extended again in C++26 – Sebastian Feb 08 '22 at 12:09
  • @NoSenseEtAl You don't need to `move` there either. That can just be `return 0;` – Barry Feb 08 '22 at 14:37
  • @Barry true, but I want to keep the optional intact for further chaining... for example, I log when I get something, but then later that same thing is getting processed... – NoSenseEtAl Feb 08 '22 at 20:29
  • @Sebastian great find, thank you, I really wish maybe_invoke existed. – NoSenseEtAl Feb 08 '22 at 20:30
  • @Caleth or @ Sebastian I think both of your comments could be an answer. – NoSenseEtAl Feb 08 '22 at 21:41

1 Answers1

8

The operations that were added to optional in C++23 are monadic in nature. That was the design of them. They fulfill certain common usage patterns that would otherwise be verbose and cumbersome to use. transform is for conditional transformations. and_then is for conditionally processing a whole new optional from an existing one. or_else is for cases where processing the lack of a value is pretty simplistic. And all of them work together to allow chaining.

But the basic act of "do something if the optional has a value" doesn't fit into this paradigm. It's not chainable. It's not monadic manipulation of the value. It's just normal use of an optional.

And doing it with if will not only be clearer as to what's going on, it'll have less noise (no lamdba and parameters, for example). There's just no reason to do that when the usual mechanism is just fine.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982