Skip to content

Instantly share code, notes, and snippets.

@dreum
Forked from LesleyLai/and_then.hpp
Last active October 26, 2018 18:04
Show Gist options
  • Save dreum/cf323ead8e048a27f49f790d0bd62313 to your computer and use it in GitHub Desktop.
Save dreum/cf323ead8e048a27f49f790d0bd62313 to your computer and use it in GitHub Desktop.
Exceptional Monad in C++ with std::optional
#ifndef AND_THEN_HPP
#define AND_THEN_HPP
#include <optional>
#include <list>
#include <algorithm>
#include <functional>
#include <type_traits>
template<typename T1,
typename Func,
typename Input_Type = typename T1::value_type,
typename T2 = std::invoke_result_t<Func, Input_Type>
>
struct monad_bind_details
{
// base template is identity monad
static constexpr T2 impl(T1 input, Func f) { return std::invoke(f, input); }
};
template<typename Func,
typename Input_Type,
typename T2
>
struct monad_bind_details<std::optional<Input_Type>, Func, Input_Type, T2>
{
// optional
static constexpr T2 impl(std::optional<Input_Type> input, Func f)
{
if (!input) return std::nullopt;
else return std::invoke(f, *input);
}
};
template<typename Func,
typename Input_Type,
typename T2
>
struct monad_bind_details<std::list<Input_Type>, Func, Input_Type, T2>
{
// list
static constexpr T2 impl(std::list<Input_Type> input, Func f)
{
T2 output;
for_each(
std::begin(input),
std::end(input),
[&output, &f](Input_Type& in)
{
auto elems = std::invoke(f, in);
output.insert(
end(output),
begin(elems),
end(elems));
});
return output;
}
};
template<typename T1,
typename Func,
typename Input_Type = typename T1::value_type,
typename T2 = std::invoke_result_t<Func, Input_Type>
>
constexpr T2 monad_bind(T1 input, Func f)
{
return monad_bind_details<T1,Func,Input_Type,T2>::impl(input, f);
}
template<typename T1,
typename Func,
typename Input_Type = typename T1::value_type,
typename T2 = std::invoke_result_t<Func, Input_Type>
>
constexpr T2 operator>>(T1 input, Func f)
{
static_assert(
std::is_invocable_v<decltype(f), Input_Type>,
"The function passed in must take type (T1::value_type) as its argument"
);
return monad_bind<T1,Func,Input_Type,T2>(input, f);
}
#endif // AND_THEN_HPP
// Example usage
#include <string>
#include <fstream>
#include <iostream>
#include "bind.hpp"
std::optional<std::string> read_file(const char* filename) {
std::ifstream file {filename};
if (!file.is_open()) {
return {};
}
std::string str((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
return {str};
}
std::optional<int> opt_stoi(std::string s) {
try {
return std::stoi(s);
} catch(std::invalid_argument e) {
return {};
} catch (std::out_of_range) {
return {};
}
}
template <typename T>
constexpr void print(std::optional<T> val) {
if (val) {
std::cout << *val << '\n';
} else {
std::cerr << "Error\n";
}
}
int main() {
auto x = read_file("exist.txt")
>> opt_stoi
>> [](int n) { return std::make_optional(n + 100); };
print(x);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment