Maybe在我的工作项目中,我反复发现自己需要Haskell风格(尤其是Maybe chaining).例如,来自客户的提款请求,我们获得客户ID ...在缓存中查找客户...如果找到客户...查找她的储蓄账户...如果有账户...退出...在任何指向此链中,如果存在查找失败,则不执行任何操作并返回失败.
我的链很大......有时只要6 ...所以这里是我Haskell.Data.Maybe在C++ 0x中的滑动...(注意......如果我停止使用可变参数模板,这应该在C++中工作).我已经开始为自由函数链接一个参数或成员函数没有参数,我对接口感到满意.但是,对于采用多个参数的函数...我必须编写一个lambda函数来模拟部分应用程序.有没有办法避免它?见最后一行main().即使它是未注释的,它也不会编译,而是用于const/non-const混合.但问题仍然存在.
对于大量的代码感到抱歉...我希望这不会让那些可能对此感兴趣的人感到震惊......
#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 >;
}
Run Code Online (Sandbox Code Playgroud)
Luc*_*ton 15
良好的开端,但我认为你的工作热情过度,让你的课堂万无一失.我个人建议'越糟越好'.首先,让我们重用Boost.Optional:
struct nothing_type {
template<typename T>
operator boost::optional<T>() const
{ return {}; }
};
constexpr nothing_type nothing;
template<typename T>
boost::optional<T>
just(T&& t)
{
return std::forward<T>(t);
}
template<typename Option, typename Functor>
auto maybe_do(Option&& option, Functor&& functor)
-> boost::optional<
decltype( functor(*std::forward<Option>(option)) )
>
{
// Forwarding
if(option)
return functor(*std::forward<Option>(option));
else
return nothing;
}
Run Code Online (Sandbox Code Playgroud)
关于不重要的事情的一些不同解释:
nothing它不一定是一个对象,它仍然可以nothing_type像你正在做的那样是一个函数(返回).那不重要.
我确保保留just与您的版本匹配的引用语义.作为奖励,它仍然可以处理价值观.因此,int i = 0; auto maybe = just(i);那时的类型maybe将是boost::optional<int&>,而auto maybe = just(42);它是boost::optional<int>.
*std::forward<Option>(option)实际上,它可以简单地*option作为Boost.Optional不是移动感知的,并且没有多少编译器支持lvalue/rvalue *this(这对它很重要).我只是喜欢面向未来的完美转发模板.
你仍然可以改名maybe_do operator|.但是我建议将它放在命名空间中并使用using ns::operator|(或using namespace ns;)将其放入范围.您可以另外(或替代)添加SFINAE检查(或写入多个重载)以确保它仅在适当的时间参与重载解析.我建议这样做是为了避免命名空间污染和烦人的错误.
重要的东西:
maybe_do与可以处理成员指针的重载相比,它可能看起来严重不足.但我建议保持简单,而不是把负担放在客户端代码上以适应成员指针:
auto maybe = /* fetch an optional<T cv ref> from somewhere */
maybe_do(maybe, std::bind(&T::some_member, _1));
Run Code Online (Sandbox Code Playgroud)
类似地,客户端代码可以std::bind用来做穷人的部分评估:
maybe_do(maybe, std::bind(some_functor, _1, "foo", _2, bar));
Run Code Online (Sandbox Code Playgroud)
我是OP(SO迁移时丢失了我的帐户).这是我提出的最新用法std::invoke.生活变得简单得多
template < typename T >
auto operator | (Maybe < T > const & v, auto && f)
{
using U = std::decay_t < decltype(f(v.get())) >;
if (v.isNull())
return Maybe < U >::nothing();
else
return Maybe < U >::just(std::invoke(f, v.get()));
}
template < typename T >
auto operator | (Maybe < T > & v, auto && f)
{
using U = std::decay_t < decltype(f(v.get())) >;
if (v.isNull())
return Maybe < U >::nothing();
else
return Maybe < U >::just(std::invoke(f, v.get()));
}
template < typename T >
auto operator | (Maybe < T > && v, auto && f)
{
using U = std::decay_t < decltype(f(v.get())) >;
if (v.isNull())
return Maybe < U >::nothing();
else
return Maybe < U >::just(std::invoke(f, v.get()));
}
Run Code Online (Sandbox Code Playgroud)