Skip to content

Instantly share code, notes, and snippets.

@smoofra
Created October 8, 2019 19:53
Show Gist options
  • Save smoofra/50efe9392e5368f9584016334c34dfce to your computer and use it in GitHub Desktop.
Save smoofra/50efe9392e5368f9584016334c34dfce to your computer and use it in GitHub Desktop.
#include <iostream>
#include <stdio.h>
// Expected monad in C++
struct Error {};
template<typename Enable, typename T, typename F, typename... Args>
struct Bind {
};
template <class T> class Expected {
public:
using value_type = T;
bool error;
T value;
Expected (T t) : value(t) {
error = false;
}
Expected (Error e) {
error = true;
}
operator bool() {
return !error;
}
T &get() {
return value;
}
Error takeError() {
return Error();
}
template<typename F, typename... Args>
typename Bind<void, T, F, Args...>::result_type bind(F f, Args && ... args) {
return Bind<void, T, F, Args...>::bind(*this, f, std::forward<Args>(args)...);
}
};
template<typename T>
struct is_expected {
static constexpr bool value = false;
};
template<typename T>
struct is_expected<Expected<T>>
{
static constexpr bool value = true;
};
template<typename T, typename F, typename... Args>
struct Bind<
typename std::enable_if<
!std::is_member_function_pointer<F>::value && is_expected<typename std::result_of<F(T, Args...)>::type>::value, void>::type,
T, F, Args...>
{
typedef typename std::result_of<F(T, Args...)>::type result_type;
static result_type bind(Expected<T> &expected, F f, Args&&... args) {
if (expected) {
return f(expected.get(), std::forward<Args>(args)...);
} else {
return Error();
}
}
};
template<typename T, typename F, typename... Args>
struct Bind<
typename std::enable_if<
!std::is_member_function_pointer<F>::value && !is_expected<typename std::result_of<F(T, Args...)>::type>::value,
void>::type,
T, F, Args...>
{
typedef typename std::result_of<F(T, Args...)>::type f_result_type;
typedef Expected<f_result_type> result_type;
static result_type bind(Expected<T> &expected, F f, Args &&... args) {
if (expected) {
return f(expected.get(), std::forward<Args>(args)...);
} else {
return Error();
}
}
};
template<typename T, typename R, typename... Args>
struct Bind<typename std::enable_if<!is_expected<R>::value, void>::type, T, R (T::*) (Args...), Args...>
{
typedef Expected<R> result_type;
static result_type bind(Expected<T> &expected, R(T::*m)(Args...), Args&&... args) {
if (expected) {
T &t = expected.get();
return (t.*m)(std::forward<Args>(args)...);
} else {
return Error();
}
}
};
template<typename T, typename R, typename... Args>
struct Bind<void, T, Expected<R> (T::*) (Args...), Args...>
{
typedef Expected<R> result_type;
static result_type bind(Expected<T> &expected, Expected<R> (T::*m)(Args...), Args && ... args) {
if (expected) {
T &t = expected.get();
return (t.*m)(std::forward<Args>(args)...);
} else {
return Error();
}
}
};
struct Foo {
int x;
Foo (int a) {
x = a;
}
int Baz() {
return x+1;
}
int Bar(int y) {
return 100 * x + y;
}
Expected<float> Quux(int y, int z) {
return 10000 * x + 100*y + z;
}
};
int main() {
Expected<int> e(42);
e.bind([](int x) -> Expected<float> { return x / 1.0;});
std::cout << e.bind([](int x) -> Expected<float> {
return x / 10.0;
}).get() << std::endl;
std::cout << e.bind([](int x) -> float {
return x * 10.0;
}).get() << std::endl;
std::cout << Expected<Foo>(Foo(99)).bind(&Foo::Baz).get() << std::endl;
std::cout << Expected<Foo>(Foo(99)).bind(&Foo::Bar, 3).get() << std::endl;
std::cout << Expected<Foo>(Foo(99)).bind(&Foo::Quux, 6, 7).get() << std::endl;
std::cout << e.bind([](int x, float y) -> Expected<float> {
return x / 10.0 + y ;
}, 0.1).get() << std::endl;
std::cout << e.bind([](int x, float y) -> float {
return x * 10.0 + y;
}, 0.2).get() << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment