Created
January 30, 2018 12:17
-
-
Save insooth/6019402654455787c022353c3edabc45 to your computer and use it in GitHub Desktop.
Maybe Monad and Currying
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 <algorithm> | |
#include <iterator> | |
#include <thread> | |
#include <iomanip> // dec | |
#include <cassert> | |
#include <cstddef> // size_t | |
#include <type_traits> // remove_reference_t | |
#include <utility> // forward, {make_,}index_sequence | |
#include <tuple> // tuple{,_size}, get | |
#include <boost/optional.hpp> // C++17: use std::optional | |
#include <boost/optional/optional_io.hpp> // printing | |
/* | |
Every filter can be understood as a function that takes an input and returns an output. | |
Chain of filters of matching in-out types is plain function composition (like g . f). | |
For non-composable functions, additional operator shall be used in lieu of . operator. | |
There is always ONE input and ONE output. Computations producing value of type T | |
that may fail shall return optional<T>. Result type CANNOT be void. | |
Want more data on input/output -- use std::tuple. | |
*/ | |
using T = int; // for exposition only! | |
template<class... Fs> | |
struct Chain | |
{ | |
using S = std::tuple<Fs...>; // sequence | |
using R = T; // TODO: compute this | |
S value; // the actual sequence | |
template<class T> struct is_tuple : std::false_type {}; | |
template<class... T> struct is_tuple<std::tuple<T...>> : std::true_type {}; | |
template<class T> struct is_optional : std::false_type {}; | |
template<class T> struct is_optional<boost::optional<T>> : std::true_type {}; | |
// Makes possible to apply tuple to the function that takes "exploded" tuple. Use C++17 apply. | |
// -- { | |
template<class F, class T, std::size_t... Is> | |
constexpr decltype(auto) mbind_impl(F&& f, T&& t, std::index_sequence<Is...>) | |
{ | |
return f(std::get<Is>(t)...); | |
} | |
/** Applies tuple t to function f. */ | |
template<class F, class T> | |
constexpr decltype(auto) mbind(F&& f, T&& t | |
, std::enable_if_t< is_tuple<std::remove_reference_t<T>>::value | |
&& ! is_optional<std::remove_reference_t<T>>::value>* = {}) | |
{ | |
return mbind_impl(std::forward<F>(f), std::forward<T>(t) | |
, std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{}); | |
} | |
// -- } | |
// implementation of monadic bind -- { | |
/** Applies non-tuple and non-optional t to function f. */ | |
template<class F, class T> | |
constexpr decltype(auto) mbind(F&& f, T&& t | |
, std::enable_if_t< ! is_tuple<std::remove_reference_t<T>>::value | |
&& ! is_optional<std::remove_reference_t<T>>::value>* = {}) | |
{ | |
return f(std::forward<T>(t)); | |
} | |
/** Applies non-tuple optional t to function f. Return boost::none if t failed. */ | |
template<class F, class T> | |
constexpr decltype(auto) mbind(F&& f, T&& t | |
, std::enable_if_t< ! is_tuple<std::remove_reference_t<T>>::value | |
&& is_optional<std::remove_reference_t<T>>::value>* = {}) | |
{ | |
return t ? boost::make_optional(f(t.get())) : boost::none; | |
} | |
// -- } | |
// -- { implementation of monadic do | |
template<std::size_t I, std::size_t Total, class As> | |
constexpr decltype(auto) mdo(As&& as | |
, std::enable_if_t<(I < Total)>* = {}) | |
{ | |
std::cout << "stage: " << I << " of " << Total << " value: " << as << "\n"; | |
return mdo<I + 1, Total>(mbind(std::get<I>(value), std::forward<As>(as))); | |
} | |
template<std::size_t I, std::size_t Total, class As> | |
constexpr auto mdo(As&& as | |
, std::enable_if_t<I == Total>* = {}) | |
{ | |
std::cout << "stage: " << I << " of " << Total << " value: " << as << "\n"; | |
return as; | |
} | |
// -- } | |
template<class As> | |
constexpr decltype(auto) operator() (As&& as) | |
{ | |
static_assert(std::tuple_size<S>::value > 0, "Empty chain"); | |
return mdo<0, std::tuple_size<S>::value>(std::forward<As>(as)); | |
// decltype(auto) r = apply(std::get<0>(value), std::forward<As>(as)); | |
// decltype(auto) r2 = apply(std::get<1>(value), r); | |
// return r2; | |
} | |
}; | |
// Flow is: g . f (g after f) where f may fail. | |
struct F | |
{ | |
bool fail = false; | |
boost::optional<T> operator() (T t) { return fail ? boost::none : boost::optional<T>{t + 1}; } | |
}; | |
struct G | |
{ | |
T operator() (T t) { return t + 1; } | |
}; | |
// IMPORTANT NOTE: if something in the middle is optional, then output is always wrapped into optional (lifted) | |
int main() | |
{ | |
Chain<F, G> chain; // TODO: add interface | |
// std::get<0>(chain.value).fail = true; | |
std::cout << chain(100) << "\n"; | |
return 0; | |
} | |
/* | |
http://coliru.stacked-crooked.com/a/4f21ac40d6183437 | |
g++ -std=c++14 -O2 -Wall -pedantic -DNDEBUG -pthread main.cpp && ./a.out | |
stage: 0 of 2 value: 100 | |
stage: 1 of 2 value: 101 | |
stage: 2 of 2 value: 102 | |
102 | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment