fenbf / cppstories-discussions

4 stars 1 forks source link

2023/monadic-optional-ops-cpp23/ #127

Open utterances-bot opened 1 year ago

utterances-bot commented 1 year ago

How to Use Monadic Operations for std::optional in C++23 - C++ Stories

In this post we’ll have a look at new operations added to std::optional in C++23. These operations, inspired by functional programming concepts, offer a more concise and expressive way to work with optional values, reducing boilerplate and improving code readability. Let’s meet and_then(), transform() and or_else(), new member functions. Traditional Approach with if/else and optional C++20   In C++20 when you work with std::optional you have to rely heavily on conditional checks to ensure safe access to the contained values.

https://www.cppstories.com/2023/monadic-optional-ops-cpp23/

2kaud commented 1 year ago

To paraphrase a well-known saying "It's C++, Bartek, but not as we know it..."

sjames1958gm commented 1 year ago

Great article, someday when our project makes it to c++23, will love using these. One comment, you make the statement "the subsequent operations are skipped" in the section Improved Error Handling. This is not entirely true, as each operation is still invoked, just the behavior is based on the std::optional. For example, or_else only calls it's callable if the std::optional is empty.

fenbf commented 1 year ago

Thanks @sjames1958gm ! I updated the text with "operations (custom callables))", maybe that's better.

ByteEater-pl commented 1 year ago

The return type should not be another std::optional.

It's worth mentioning IMO that it works just fine, creating nested std::optional. And the "should not" admonition sounds strange to functional programmers who do such stuff all the time. In these times of growing influence of functional programming on (among others) C++, as this very article exemplifies, a more open embrace thereof would be appreciated by me and probably many others who like this style newly available in C++.

U must not be std::in_place_t or std::nullopt_t.

Probably most of your readers are C++ veterans for whom the reasons of these restrictions are obvious. But I still dare ask, hoping for an answer: why?

phoenicyan commented 1 year ago

the same can be written in old way (i used boost 1.70):

int main()
{
    const int userId = 12345;
    std::optional<int> age;
    std::optional<UserProfile> profile = fetchFromCache(userId) ? fetchFromCache(userId) : fetchFromServer(userId);
    cout << (profile && (age = extractAge(*profile)) 
        ? format("Next year, the user will be {} years old", *age + 1)
        : "Failed to determine user's age.\n");
}

in my experience, the functional way of programming could be handy sometimes, but practically not used much. Sometimes I think - stop beating the dead horse (i.e add unnecessary features to c++), instead switch to python ;)

cdriper commented 1 year ago

doesn't have many sense w/o coroutines/async support

PiotrNycz commented 3 months ago

What is the best way to use monadic API for operations requiring 2+ values stored in optionals.

Just for example purposes - assume we have "int func(int, int);" and "optional opt1, opt2;"

Is the following way the only way, or we can do shorter/more readable?:

auto res = opt1.and_then([&opt2](auto v1) 
      { 
             return opt2.transform([v1](auto v2){ return func(v1, v2); });
     }).value_or(-1);

Imagine you have an operation that requires 5 values stored in optionals, then sequence of and_then,..and_then...transform... with lots of lambdas and captures can be quite challenging comparing to:


if (opt1 && opt2 && opt3 && opt4 && opt5)
{
     return func(*opt1, *opt2, *opt3, *opt4, *opt5);
}
return -1;