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
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
.