CodeNebula
C++23stlexpert

std::optional Monadic Operations (C++23 Addition)

C++23 · Completing Modern · Compiler flag: -std=c++23

Explanation

Adds `and_then`, `transform`, and `or_else` for monadic-style operations.

Real-World Scenario

Chain optional operations in a configuration parser.

Quick Snippet

snippet.cpp
#include <optional> auto result = opt.transform([](int x) { return x * x; });

Full Example

optional_monadic.cpp
// CodeNebula - C++23 Example: std::optional Monadic Operations
// Compile: g++ -std=c++23 -Wall -Wextra -o example optional_monadic.cpp
// Note: Requires GCC 13+ / Clang 17+

#include <iostream>
#include <optional>
#include <string>
#include <charconv>

// C++23 adds monadic operations to std::optional:
//   and_then(f) - if has value, call f(value) returning optional (flatmap)
//   transform(f) - if has value, call f(value) and wrap result in optional (map)
//   or_else(f)   - if empty, call f() returning optional (fallback)

// Simulated database/parsing functions returning optional
std::optional<std::string> find_user(int id) {
    if (id == 1) return "Alice";
    if (id == 2) return "Bob";
    return std::nullopt;
}

std::optional<std::string> find_email(const std::string& name) {
    if (name == "Alice") return "alice@example.com";
    return std::nullopt;
}

std::optional<int> parse_int(const std::string& s) {
    int value{};
    auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value);
    if (ec == std::errc{} && ptr == s.data() + s.size())
        return value;
    return std::nullopt;
}

int main() {
    std::cout << "=== std::optional monadic operations ===\n\n";

    // --- and_then: chain optional-returning functions ---
    std::cout << "--- and_then (flatmap) ---\n";
    auto email1 = find_user(1).and_then(find_email);
    std::cout << "User 1 email: " << email1.value_or("(not found)") << "\n";

    auto email2 = find_user(2).and_then(find_email);
    std::cout << "User 2 email: " << email2.value_or("(not found)") << "\n";

    auto email3 = find_user(99).and_then(find_email);
    std::cout << "User 99 email: " << email3.value_or("(not found)") << "\n\n";

    // --- transform: apply function to value inside optional ---
    std::cout << "--- transform (map) ---\n";
    auto name_len = find_user(1).transform([](const std::string& s) {
        return s.size();
    });
    std::cout << "User 1 name length: " << name_len.value_or(0) << "\n";

    auto upper_name = find_user(1).transform([](std::string s) {
        for (auto& c : s) c = static_cast<char>(std::toupper(c));
        return s;
    });
    std::cout << "User 1 upper: " << upper_name.value_or("N/A") << "\n\n";

    // --- or_else: provide fallback ---
    std::cout << "--- or_else (fallback) ---\n";
    auto user = find_user(99).or_else([]() -> std::optional<std::string> {
        std::cout << "  (user not found, using default)\n";
        return "Guest";
    });
    std::cout << "Result: " << user.value() << "\n\n";

    // --- Chaining all three ---
    std::cout << "--- Chaining and_then + transform + or_else ---\n";
    auto result = parse_int("42")
        .and_then([](int v) -> std::optional<int> {
            return v > 0 ? std::optional{v * 2} : std::nullopt;
        })
        .transform([](int v) { return "Result: " + std::to_string(v); })
        .or_else([]() -> std::optional<std::string> { return "fallback"; });

    std::cout << result.value() << "\n";

    auto bad = parse_int("abc")
        .transform([](int v) { return v * 2; })
        .or_else([]() -> std::optional<int> { return -1; });
    std::cout << "parse_int(\"abc\") chain: " << bad.value() << "\n";

    return 0;
}

// Expected output:
// === std::optional monadic operations ===
//
// --- and_then (flatmap) ---
// User 1 email: alice@example.com
// User 2 email: (not found)
// User 99 email: (not found)
//
// --- transform (map) ---
// User 1 name length: 5
// User 1 upper: ALICE
//
// --- or_else (fallback) ---
//   (user not found, using default)
// Result: Guest
//
// --- Chaining and_then + transform + or_else ---
// Result: 84
// parse_int("abc") chain: -1
Run command
g++ -std=c++23 -Wall -Wextra -o example optional_monadic.cpp