#include <iostream>

#define TRY(Expr_) \
    ({ auto result = (Expr_); \
       if (!result.ok) { return result; } \
       std::move(result.data); })

template <typename E>
struct Error {
	Error(E e): error(std::move(e)) {}
	
	E error;
};

template <typename E>
Error<E> error(E e) { return Error<E>(std::move(e)); }

template <typename T, typename E>
struct Result {
	template <typename U>
	Result(U u): ok(true), data(std::move(u)), error() {}
	
	template <typename F>
	Result(Error<F> f): ok(false), data(), error(std::move(f.error)) {}
	
	template <typename U, typename F>
	Result(Result<U, F>&& other):
	    ok(other.ok), data(std::move(other.data)),  error(std::move(other.error)) {}
	
	
	bool ok = false;
	T data;
	E error;
};


Result<double, std::string> sqrt(double x) {
	if (x < 0) {
		return error("sqrt does not accept negative numbers");
	}
	return x;
}

Result<double, std::string> double_sqrt(double x) {
	auto y = TRY(sqrt(x));
	return sqrt(y);
}

int main() {
	double_sqrt(4);
	return 0;
}