Implementing Haskell's Maybe Monad in c++11

后端 未结 6 1465
星月不相逢
星月不相逢 2021-01-31 09:54

I am trying to implement the Maybe monad from Haskell using the lambda functions in C++11 and templates. Here\'s what I have so far

#include
#i         


        
6条回答
  •  旧时难觅i
    2021-01-31 10:54

    Literally copy & pasting from Haskell style "Maybe" type & *chaining* in C++11

    This is probably what you really want to achieve

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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 >;
    }
    

提交回复
热议问题