Created
December 2, 2020 06:52
-
-
Save cleoold/0f1465f21a926abc37f8334c5f480c34 to your computer and use it in GitHub Desktop.
maybe type with funny operator overloading
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <variant> | |
#include <tuple> | |
// https://gist.github.com/cleoold/df31c3789f2b2b8fe2cfa5022957ee06 | |
#include "lambda_traits.hpp" | |
template<class T> | |
struct decay_maybe { using type = T; }; | |
template<class T> | |
class Just { | |
T value; | |
public: | |
template<class Arg> | |
constexpr Just(Arg &&v) : value(std::forward<Arg>(v)) {} | |
auto constexpr unwrap() { | |
return std::move(value); | |
} | |
template<class F> | |
friend auto constexpr operator>>=(const Just &just, F &&f) { | |
return std::forward<F>(f)(just.value); | |
} | |
friend auto &operator<<(std::ostream &os, const Just &just) { | |
os << "Just<" << just.value << '>'; | |
return os; | |
} | |
template<class F1, class F2> | |
requires (is_lambda<std::remove_cvref_t<F1>>::value && is_lambda<std::remove_cvref_t<F2>>::value) | |
friend auto operator>>=(F1 &&f1, F2 &&f2); | |
}; | |
template<class T> | |
Just(T) -> Just<T>; | |
class Nothing { | |
public: | |
template<class F> | |
friend auto constexpr operator>>=(const Nothing ¬hing, F &&f) { | |
return nothing; | |
} | |
friend auto &operator<<(std::ostream &os, const Nothing &) { | |
os << "Nothing"; | |
return os; | |
} | |
}; | |
template<class T> | |
class Maybe { | |
std::variant<Just<T>, Nothing> value; | |
public: | |
template<class Arg> | |
constexpr Maybe(Arg &&v) : value(std::forward<Arg>(v)) {} | |
constexpr Maybe() : value{Nothing{}} {} | |
auto constexpr unwrap(const T &default_) { | |
return std::visit([&default_] (auto &&v) { | |
using TVariant = std::remove_cvref_t<decltype(v)>; | |
if constexpr (std::is_same_v<TVariant, Just<T>>) | |
return v.unwrap(); | |
else | |
return default_; | |
}, value); | |
} | |
template<class F> | |
friend auto constexpr operator>>=(const Maybe &maybe, F &&f) { | |
// if func returns Nothing directly, do not make Maybe<Nothing>(Nothing()) | |
// instead inherit the same type T: Maybe<T>(Nothing()) | |
using TestR = typename decay_maybe<decltype(f(std::declval<T>()))>::type; | |
using R = std::conditional_t<std::is_same_v<TestR, Nothing>, T, TestR>; | |
return std::visit([&f] (auto &&v) -> Maybe<R> { | |
return std::forward<decltype(v)>(v) >>= f; | |
}, maybe.value); | |
} | |
friend auto &operator<<(std::ostream &os, const Maybe &maybe) { | |
std::visit([&os] (auto &&v) { | |
os << "Maybe<" << std::forward<decltype(v)>(v) << '>'; | |
}, maybe.value); | |
return os; | |
} | |
template<class F1, class F2> | |
requires (is_lambda<std::remove_cvref_t<F1>>::value && is_lambda<std::remove_cvref_t<F2>>::value) | |
friend auto operator>>=(F1 &&f1, F2 &&f2); | |
}; | |
template<class T> | |
Maybe(T) -> Maybe<T>; | |
template<class T> | |
struct decay_maybe<Maybe<T>> { using type = T; }; | |
template<class T> | |
struct decay_maybe<Just<T>> { using type = T; }; | |
template<class T> | |
struct is_just { static constexpr auto value = false; }; | |
template<class T> | |
struct is_just<Just<T>> { static constexpr auto value = true; }; | |
template<class T> | |
struct is_maybe { static constexpr auto value = false; }; | |
template<class T> | |
struct is_maybe<Maybe<T>> { static constexpr auto value = true; }; | |
////////////////////////////////////////////////////// | |
template<class F1, class F2> | |
requires (is_lambda<std::remove_cvref_t<F1>>::value && is_lambda<std::remove_cvref_t<F2>>::value) | |
auto operator>>=(F1 &&f1, F2 &&f2) { | |
using F1Traits = lambda_traits<std::remove_const_t<std::remove_reference_t<F1>>>; | |
using F1Arg = typename F1Traits::template arg_type_at<0>; | |
using F1Ret = typename F1Traits::result_type; | |
using F2Traits = lambda_traits<std::remove_const_t<std::remove_reference_t<F2>>>; | |
using F2Ret = typename F2Traits::result_type; | |
return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] (F1Arg x) { | |
auto f1ret = f1(std::forward<decltype(x)>(x)); | |
if constexpr (std::is_same_v<F1Ret, Nothing>) | |
return f1ret; | |
else if constexpr (is_just<F1Ret>::value) | |
return f2(f1ret.value); | |
else if constexpr (is_maybe<F1Ret>::value) { | |
using F2Ret = typename decay_maybe<F2Ret>::type; | |
using R = std::conditional_t<std::is_same_v<F2Ret, Nothing>, F1Ret, F2Ret>; | |
return std::visit([f2] (auto &&v) -> Maybe<R> { | |
using VT = std::remove_cvref_t<decltype(v)>; | |
if constexpr (std::is_same_v<VT, Nothing>) | |
return v; | |
else | |
return f2(std::forward<decltype(v)>(v).value); | |
}, f1ret.value); | |
} else | |
return f2(f1ret); | |
}; | |
} | |
////////////////////////////////////////////////////// | |
#define fn [&] | |
#define π‘(...) { return (__VA_ARGS__); } | |
int main() { | |
// return values directly | |
auto val = Maybe(16) | |
>>= fn (int x) π‘ (x + 1) | |
>>= fn (int x) π‘ (x * 2) | |
>>= fn (int x) π‘ (x - 4.4); | |
auto val2 = Maybe(16) | |
>>= fn (int x) π‘ (x + 1) | |
>>= fn (int) π‘ (Nothing()) | |
>>= fn (int x) π‘ (x - 4.4); | |
std::cout << val << std::endl; | |
std::cout << " " << val.unwrap(0) << std::endl; | |
std::cout << val2 << std::endl; | |
std::cout << " " << val2.unwrap(0) << std::endl; | |
// return maybes | |
auto val3 = Maybe(17) | |
>>= fn (int x) π‘ (Maybe(x + 10)) | |
>>= fn (int x) π‘ (x > 18 ? Maybe(x * 2.2) : Maybe<double>()) | |
>>= fn (double x) π‘ (Maybe(std::to_string(x) + "lf")); | |
auto val4 = Maybe(7) | |
>>= fn (int x) π‘ (Maybe(x + 10)) | |
>>= fn (int x) π‘ (x > 18 ? Maybe(x * 2.2) : Maybe<double>()) | |
>>= fn (double x) π‘ (Maybe(std::to_string(x) + "lf")); | |
std::cout << val3 << std::endl; | |
std::cout << " " << val3.unwrap("none") << std::endl; | |
std::cout << val4 << std::endl; | |
std::cout << " " << val4.unwrap("none") << std::endl; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Maybe<Just<29.6>> | |
29.6 | |
Maybe<Nothing> | |
0 | |
Maybe<Just<59.400000lf>> | |
59.400000lf | |
Maybe<Nothing> | |
none |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment