Буквально копировать и вставлять из стиль Haskell Возможно тип и *цепочка* в С++ 11
Это, вероятно, то, чего вы действительно хотите достичь
#include <iostream>
#include <map>
#include <deque>
#include <algorithm>
#include <type_traits>
typedef long long int int64;
namespace monad { namespace maybe {
struct Nothing {};
template < typename T >
struct Maybe {
template < typename U, typename Enable = void >
struct ValueType {
typedef U * const type;
};
template < typename U >
struct ValueType < U, typename std::enable_if < std::is_reference < U >::value >::type > {
typedef typename std::remove_reference < T >::type * const type;
};
typedef typename ValueType < T >::type value_type;
value_type m_v;
Maybe(Nothing const &) : m_v(0) {}
struct Just {
value_type m_v;
Just() = delete;
explicit Just(T &v) : m_v(&v) {
}
};
Maybe(Just const &just) : m_v(just.m_v) {
}
};
Nothing nothing() {
return Nothing();
}
template < typename T >
Maybe < T > just(T &v) {
return typename Maybe < T >::Just(v);
}
template < typename T >
Maybe < T const > just(T const &v) {
return typename Maybe < T const >::Just(v);
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A const &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A const &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...)) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...)) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}
template < typename T, typename A >
void operator | (Maybe < T > const &t, void (*f)(A const &)) {
if (t.m_v)
f(*t.m_v);
}
}}
struct Account {
std::string const m_id;
enum Type { CHECKING, SAVINGS } m_type;
int64 m_balance;
int64 withdraw(int64 const amt) {
if (m_balance < amt)
m_balance -= amt;
return m_balance;
}
std::string const &getId() const {
return m_id;
}
};
std::ostream &operator << (std::ostream &os, Account const &acct) {
os << "{" << acct.m_id << ", "
<< (acct.m_type == Account::CHECKING ? "Checking" : "Savings")
<< ", " << acct.m_balance << "}";
}
struct Customer {
std::string const m_id;
std::deque < Account > const m_accounts;
};
typedef std::map < std::string, Customer > Customers;
using namespace monad::maybe;
Maybe < Customer const > getCustomer(Customers const &customers, std::string const &id) {
auto customer = customers.find(id);
if (customer == customers.end())
return nothing();
else
return just(customer->second);
};
Maybe < Account const > getAccountByType(Customer const &customer, Account::Type const type) {
auto const &accounts = customer.m_accounts;
auto account = std::find_if(accounts.begin(), accounts.end(), [type](Account const &account) -> bool { return account.m_type == type; });
if (account == accounts.end())
return nothing();
else
return just(*account);
}
Maybe < Account const > getCheckingAccount(Customer const &customer) {
return getAccountByType(customer, Account::CHECKING);
};
Maybe < Account const > getSavingsAccount(Customer const &customer) {
return getAccountByType(customer, Account::SAVINGS);
};
int64 const &getBalance(Account const &acct) {
return acct.m_balance;
}
template < typename T >
void print(T const &v) {
std::cout << v << std::endl;
}
int main(int const argc, char const * const argv[]) {
Customers customers = {
{ "12345", { "12345", { { "12345000", Account::CHECKING, 20000 }, { "12345001", Account::SAVINGS, 117000 } } } }
, { "12346", { "12346", { { "12346000", Account::SAVINGS, 1000000 } } } }
};
getCustomer(customers, "12346") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getSavingsAccount | &Account::getId | &print < std::string const >;
// getCustomer(customers, "12345") | getSavingsAccount | [](Account &acct){ return acct.withdraw(100); } | &print < std::string const >;
}
person
zrb
schedule
18.09.2014
Maybe
C++. - person Xeo   schedule 14.03.2012>>=
в C++ имеет неправильную ассоциативность. Вы лучше поймете монады, если вам не придется биться головой о совершенно не связанные между собой стены в процессе. Попробуйте изучить монады в Haskell. - person R. Martinho Fernandes   schedule 14.03.2012>>=
не имеет ничего общего с монадами. Это оператор, используемый в Haskell для выражения операции привязки монад, но это просто решение, принятое разработчиками языка Haskell. Использование этого конкретного оператора не является неотъемлемой частью монады. Так что нет смысла пытаться реализовать монады на C++ с синтаксисом Haskell. Реализуйте монады на C++ удобным для C++ способом, вместо того, чтобы слепо копировать совершенно произвольные проектные решения, принятые в Haskell. - person jalf   schedule 14.03.2012do
то, как это работает на практике... не очень практично. По крайней мере, пожалуйста, пересмотрите использование>>=
:m >>= f >>= g
в C++ — этоm >>= (f >>= g)
, а в Haskell —(m >>= f) >>= g
. - person R. Martinho Fernandes   schedule 14.03.2012>>=
произносится какbind
, так что я предлагаю вам использовать его. Кроме того, посколькуreturn
является ключевым словом C++, вы можете позаимствовать имяunit
из теории категорий. - person Vitus   schedule 14.03.2012bind
на языке Haskell. В C++ это произносится как оператор присваивания сдвига вправо или что-то в этом роде. Это была именно моя точка зрения. Если вы хотите реализовать монаду, вам нужна операция bind. Но операция bind не обязательно должна быть представлена оператором>>=
. Эта ассоциация существует только в Haskell, это не универсальное требование или что-то, что вы должны иметь, чтобы что-то было монадой. - person jalf   schedule 14.03.2012>>=
в C++ не очень удачная идея, поэтому я предложил лучшее название для функции (bind
происходит от>>=
в Haskell). - person Vitus   schedule 16.03.2012