261.md

September 5, 2022 ยท View on GitHub

Info

Example

int main() {
  auto opt = std::optional{42};
  opt.and_then([](auto o)->std::optional<int>{ std::cout << o;; return std::nullopt; });// prints 42
}

https://godbolt.org/z/aeKWEa63b

Puzzle

  • Can you refactor execute routine with monadic Monadic operations?
struct market_data{};
struct trade{};
struct order{};
struct order_with_id{};

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

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

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

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

	should("produce bail out on market data") = [] {
		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(not execute(fake_ts, {}).has_value());
	};

	should("produce bail out on 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;

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

	should("produce bail out on map") = [] {
		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(not execute(fake_ts, {}).has_value());
	};

	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/oG4qMPxEe

Solutions

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

https://godbolt.org/z/z36z3f9cd

std::optional<order_with_id> execute(auto& ts, const market_data& md) {
    auto model = [&ts](const auto &t){ return ts.model(t); };
    auto map = [&ts](const auto &o){ return ts.map(o); };

    return ts.parse(md)
        .and_then(model)
        .and_then(map);
}

https://godbolt.org/z/se6MYh11q

decltype(auto) operator|(auto&& x, auto&& p) { return x.and_then(p); }

std::optional<order_with_id> execute(auto& ts, const market_data& md) {
  return ts.parse(md) | [&ts](auto o) { return ts.model(o); } |
         [&ts](auto o) { return ts.map(o); };
}

https://godbolt.org/z/GEf5j6zej