Implementing Haskell's Maybe Monad in c++11

后端 未结 6 1452
星月不相逢
星月不相逢 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条回答
  •  囚心锁ツ
    2021-01-31 10:36

    Here's my maybe "monad" that I use quite often in my C++ projects (disclaimer: see the comments below). It's insofar more like the Haskell Maybe than your implementation as it only holds an object in the just case (points mobj on it), not wasting space if it's nothing. This also allows it to use of C++11 move semantics, to avoid unnecessary copies. The return types of fmap (fmapped member function) and >>= are deduced with decltype.

    template
    class maybe;
    template
    maybe just(const DataT &obj);
    struct nothing_object{nothing_object(){}};
    const nothing_object nothing;
    
                     //template class objects of which may or may not contain some given
                    // data object. Inspired by Haskell's Maybe monad.
    template
    class maybe {
      DataT *obj;
    
     public:
    
      class iterator {
        DataT *mobj;
        explicit iterator(DataT *init):mobj(init){}
       public:
        iterator():mobj(nullptr){}
        iterator(const iterator &cp):mobj(cp.mobj){}
        bool operator!=(const iterator &other)const{return mobj!=other.mobj;}
        DataT &operator*() const{return *mobj;}
        iterator &operator++(){ mobj=nullptr; return *this; }
        friend class maybe;
      };
      class const_iterator {
        const DataT *mobj;
        explicit const_iterator(const DataT *init):mobj(init){}
       public:
        const_iterator():mobj(nullptr){}
        const_iterator(const const_iterator &cp):mobj(cp.mobj){}
        bool operator!=(const const_iterator &other)const{return mobj!=other.mobj;}
        const DataT &operator*() const{return *mobj;}
        const_iterator &operator++(){ mobj=nullptr; return *this; }
        friend class maybe;
      };
      iterator begin(){return iterator(obj);}
      iterator end(){return iterator();}
      const_iterator begin()const{return const_iterator(obj);}
      const_iterator end()const{return const_iterator();}
      const_iterator c_begin()const{return const_iterator(obj);}
      const_iterator c_end()const{return const_iterator();}
    
      bool is_nothing()const{return obj==nullptr;}
      void make_nothing(){delete obj; obj=nullptr;}
      bool is_just()const{return obj!=nullptr;}
      template
      void with_just_assign(CpDataT &mdftg)const{if(obj) mdftg=*obj;}
      DataT &from_just(){return *obj;}
      DataT &operator*(){return *obj;}
      const DataT &from_just()const{return *obj;}
      const DataT &operator*()const{return *obj;}
    
      template
      bool operator==(const maybe &cmp)const{
        return is_just()==cmp.is_just() && (is_nothing() || *obj==*cmp.obj); }
      template
      bool operator!=(const maybe &cmp)const{
        return is_just()!=cmp.is_just() || (is_just() && *obj!=*cmp.obj); }
      bool operator==(const nothing_object &n)const{return obj==nullptr;}
      bool operator!=(const nothing_object &n)const{return obj!=nullptr;}
    
      template
      auto fmapped(MpFnT f) const -> maybe {
        return obj? just(f(*obj)) : nothing;                  }
      template
      auto operator>>=(MonadicFn f) const -> decltype(f(*obj)) {
        return obj? f(*obj) : nothing;                         }
      template
      auto operator>>(const maybe &r) const -> maybe {
        return obj? r : nothing;                                           }
      auto operator>>(const nothing_object &n) const -> maybe {
        return nothing;                                              }
    
    
      maybe(const nothing_object &n):obj(nullptr){}
      template
      explicit maybe(const CpDataT &cobj):obj(new DataT(cobj)){}
      template
      maybe &operator=(const CpDataT &cobj){delete obj; obj=new DataT(cobj); return *this;}
      template
      maybe(const maybe &cp):obj(cp.is_just()?new DataT(cp.from_just()):nullptr){}
      template
      maybe &operator=(const maybe &cp){
        delete obj;  obj = cp.is_just()? new DataT(cp.from_just()) : nullptr; return *this;}
      maybe(maybe &&mv):obj(mv.obj){mv.obj=nullptr;}
      maybe &operator=(maybe &&mv) {
        delete obj; obj=mv.obj; mv.obj=nullptr; return *this; }
    
      ~maybe(){delete obj;}
    };
    
    template
    auto just(const DataT &obj) -> maybe {return maybe(obj);}
    
    template              // represents Haskell's <$> infix
    auto operator^(MpFnT f, const maybe &m) -> decltype(m.fmapped(f)) {
      return m.fmapped(f);
    }
    
    template
    auto joined(const maybe> &m) -> maybe {
      return m.is_just()? m.from_just() : nothing;
    }
    
    
    template
    auto maybe_yes(const std::pair& mbcst) -> maybe {
      return mbcst.second ? just(mbcst.first) : nothing;
    }
    template
    auto maybe_not(const std::pair& mbcst) -> maybe {
      return !mbcst.second ? just(mbcst.first) : nothing;
    }
    

    The somewhat strange-seeming begin and end iterators allow it to be used in C++11 range-based for loops:

    maybe a = just(7), b = nothing;
    
    for (auto&i: a) std::cout << i;
    for (auto&i: b) std::cout << i;
    

    outputs only once 7.

提交回复
热议问题