Skip to content

Latest commit

 

History

History
229 lines (190 loc) · 6.46 KB

322.md

File metadata and controls

229 lines (190 loc) · 6.46 KB
Info

Example

enum class error { runtime_error };

[[nodiscard]] auto foo() -> std::expected<int, error> {
    return std::unexpected(error::runtime_error);
}

int main() {
    const auto e = foo();
    e.and_then([](const auto& e) -> std::expected<int, error> {
         std::cout << int(e);  // not printed
         return {};
     }).or_else([](const auto& e) -> std::expected<int, error> {
        std::cout << int(e);  // prints 0
        return {};
    });
}

https://godbolt.org/z/MeoP5r3xP

Puzzle

Can you refactor execute routine with Monadic std::expected operations?

enum class error {
    trade,
    model,
    order_id,
};

struct market_data {};
struct trade {};
struct order {};
struct order_with_id {};

std::expected<order_with_id, error> execute(auto& ts, const market_data& md) {
    auto trade = ts.parse(md);
    if (not trade) {
        return std::unexpected(error::trade);
    }

    auto order = ts.model(*trade);
    if (not order) {
        return std::unexpected(error::model);
    }

    if (auto order_with_id = ts.map(*order); order_with_id) {
        return *order_with_id;
    } else {
        return std::unexpected(error::order_id);
    }
}

int main() {
    using namespace boost::ut;

    should("error on empty trade") = [] {
        struct {
            auto parse(const market_data&) -> std::optional<trade> {
                return {};
            }
            auto model(const trade&) -> std::optional<order> { return {{}}; }
            auto map(const order&) -> std::optional<order_with_id> {
                return {{}};
            }
        } fake_ts;

        const auto e = execute(fake_ts, {});
        expect(not e and error::trade == e.error());
    };

    should("error on empty model") = [] {
        struct {
            auto parse(const market_data&) -> std::optional<trade> {
                return {{}};
            }
            auto model(const trade&) -> std::optional<order> { return {}; }
            auto map(const order&) -> std::optional<order_with_id> {
                return {{}};
            }
        } fake_ts;

        const auto e = execute(fake_ts, {});
        expect(not e and error::model == e.error());
    };

    should("error on order_id") = [] {
        struct {
            auto parse(const market_data&) -> std::optional<trade> {
                return {{}};
            }
            auto model(const trade&) -> std::optional<order> { return {{}}; }
            auto map(const order&) -> std::optional<order_with_id> {
                return {};
            }
        } fake_ts;

        const auto e = execute(fake_ts, {});
        expect(not e and error::order_id == e.error());
    };

    should("produce an order") = [] {
        struct {
            auto parse(const market_data&) -> std::optional<trade> {
                return {{}};
            }
            auto model(const trade&) -> std::optional<order> { return {{}}; }
            auto map(const order&) -> std::optional<order_with_id> {
                return {{}};
            }
        } fake_ts;

        expect(execute(fake_ts, {}).has_value());
    };
}

https://godbolt.org/z/1esfP8KjE

Solutions

std::expected<order_with_id, error> execute(auto &ts, const market_data &md) {
    constexpr auto expected_or = []<class T>(const std::optional<T> &e,
                                             error u) {
        return e ? std::expected<T, error>(*e) : std::unexpected(u);
    };
    return expected_or(ts.parse(md), error::trade)
        .and_then([&](const auto &trade) {
            return expected_or(ts.model(trade), error::model);
        })
        .and_then([&](const auto &order) {
            return expected_or(ts.map(order), error::order_id);
        });
}

https://godbolt.org/z/qh8W3oa4v

std::expected<order_with_id, error> execute(auto& ts, const market_data& md) {
    auto as_expected = []<typename T>(const std::optional<T>& o,
                                      error e) -> std::expected<T, error> {
        return o ? std::expected<T, error>(*o) : std::unexpected(e);
    };

    return as_expected(ts.parse(md), error::trade)
        .and_then([&](const auto& trade) {
            return as_expected(ts.model(trade), error::model);
        })
        .and_then([&](const auto& order) {
            return as_expected(ts.map(order), error::order_id);
        });
}

https://godbolt.org/z/eM3sdr9Ef

template <class T>
constexpr std::expected<T, error> to_expected(std::optional<T> t, error e) {
    if (t) {
        return *t;
    } else {
        return std::unexpected(e);
    }
};

std::expected<order_with_id, error> execute(auto& ts, const market_data& md) {
    return to_expected(ts.parse(md), error::trade)
        .and_then([&](const auto& trade) {
            return to_expected(ts.model(trade), error::model);
        })
        .and_then([&](const auto& order) {
            return to_expected(ts.map(order), error::order_id);
        });
}

https://godbolt.org/z/d4MTsrbP1

std::expected<order_with_id, error> execute(auto& ts, const market_data& md) {
    auto to_expected = []<typename T>(std::optional<T> o,
                                      error e) -> std::expected<T, error> {
        return o.has_value() ? std::expected<T, error>{*o} : std::unexpected{e};
    };
    return to_expected(ts.parse(md), error::trade)
        .and_then(
            [&](auto v) { return to_expected(ts.model(v), error::model); })
        .and_then(
            [&](auto v) { return to_expected(ts.map(v), error::order_id); });
}

https://godbolt.org/z/EMfx36G4K

template <typename T>
constexpr std::expected<T, error> optional_to_expected(
    const std::optional<T>&& o, const error e) {
    return o.has_value() ? std::expected<T, error>(o.value())
                         : std::unexpected(e);
}

std::expected<order_with_id, error> execute(auto& ts, const market_data& md) {
    return optional_to_expected(ts.parse(md), error::trade)
        .and_then([&](const auto& trade) {
            return optional_to_expected(ts.model(trade), error::model);
        })
        .and_then([&](const auto& order) {
            return optional_to_expected(ts.map(order), error::order_id);
        });
}

https://godbolt.org/z/YMM4qeW78