Skip to content

Instantly share code, notes, and snippets.

@dpwright
Last active June 21, 2018 03:09
Show Gist options
  • Select an option

  • Save dpwright/6474401 to your computer and use it in GitHub Desktop.

Select an option

Save dpwright/6474401 to your computer and use it in GitHub Desktop.

Revisions

  1. dpwright revised this gist Sep 9, 2013. 2 changed files with 43 additions and 33 deletions.
    39 changes: 39 additions & 0 deletions maybe.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    //maybe.h
    //
    //The Maybe type.
    //Provides a safer way to return possible failure than NULL values. Of course,
    //since C++ doesn't have pattern matching, it is down to you to ensure that you
    //always call isJust() to confirm there is a value available before calling
    //fromJust() to retrieve it.

    #pragma once

    #include <cassert>

    template<typename a> class Maybe
    {
    public:
    static const Maybe<a> Just(const a value) { return Maybe(value); }
    static const Maybe<a> Nothing() { return Maybe(); }

    const bool isJust() const { return m_valid; }
    const a fromJust() const { assert(isJust());
    return m_value; }

    private:
    Maybe() : m_valid(false) {}
    Maybe(const a value) : m_value(value), m_valid(true) {}
    Maybe(const a value, const bool valid) : m_value(value), m_valid(valid) {}
    a m_value;
    const bool m_valid;
    };

    template<typename a> const Maybe<a> Just(const a value)
    {
    return Maybe<a>::Just(value);
    }

    template<typename a> const Maybe<a> Nothing()
    {
    return Maybe<a>::Nothing();
    }
    37 changes: 4 additions & 33 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -1,42 +1,13 @@
    //maybem.h
    //
    //The Maybe monad.
    //Provides a safer way to return possible failure than NULL values. Of course,
    //since C++ doesn't have pattern matching, it is down to you to ensure that you
    //always call isJust() to confirm there is a value available before calling
    //fromJust() to retrieve it.
    //Adds monadic behaviour to Maybe. Allows you to sequence a number of
    //operations of type Maybe<a>, and drop out at the first point of failure.

    #pragma once

    #include <cassert>

    template<typename a> class Maybe
    {
    public:
    static const Maybe<a> Just(const a value) { return Maybe(value); }
    static const Maybe<a> Nothing() { return Maybe(); }

    const bool isJust() const { return m_valid; }
    const a fromJust() const { assert(isJust());
    return m_value; }

    private:
    Maybe() : m_valid(false) {}
    Maybe(const a value) : m_value(value), m_valid(true) {}
    Maybe(const a value, const bool valid) : m_value(value), m_valid(valid) {}
    a m_value;
    const bool m_valid;
    };

    template<typename a> const Maybe<a> Just(const a value)
    {
    return Maybe<a>::Just(value);
    }

    template<typename a> const Maybe<a> Nothing()
    {
    return Maybe<a>::Nothing();
    }
    #include "monad.h"
    #include "maybe.h"

    template<> struct Monad<Maybe>
    {
  2. dpwright revised this gist Sep 9, 2013. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -33,9 +33,9 @@ template<typename a> const Maybe<a> Just(const a value)
    return Maybe<a>::Just(value);
    }

    template<typename a> a Nothing()
    template<typename a> const Maybe<a> Nothing()
    {
    return a::Nothing();
    return Maybe<a>::Nothing();
    }

    template<> struct Monad<Maybe>
    @@ -49,5 +49,6 @@ template<> struct Monad<Maybe>
    template<typename a, typename b>
    auto operator>>=(const Maybe<a>&& in, const b&& f) -> decltype(f(in.fromJust()))
    {
    return in.isJust() ? f(in.fromJust()) : Nothing<decltype(f(in.fromJust()))>();
    typedef decltype(f(in.fromJust())) maybeType;
    return in.isJust() ? f(in.fromJust()) : maybeType::Nothing();
    }
  3. dpwright revised this gist Sep 9, 2013. 2 changed files with 12 additions and 5 deletions.
    10 changes: 8 additions & 2 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -20,8 +20,6 @@ template<typename a> class Maybe
    const a fromJust() const { assert(isJust());
    return m_value; }

    static const Maybe<a> unit(a x) { return Maybe(x); }

    private:
    Maybe() : m_valid(false) {}
    Maybe(const a value) : m_value(value), m_valid(true) {}
    @@ -40,6 +38,14 @@ template<typename a> a Nothing()
    return a::Nothing();
    }

    template<> struct Monad<Maybe>
    {
    template<typename a> static const Maybe<a> unit (a value)
    {
    return Just(value);
    }
    };

    template<typename a, typename b>
    auto operator>>=(const Maybe<a>&& in, const b&& f) -> decltype(f(in.fromJust()))
    {
    7 changes: 4 additions & 3 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,14 @@
    //monad.h
    //
    //Defines the two monad operations. Every class which wants to be treated as a
    //monad must either provide a static function called "unit" or a specialization
    //for the Monad class, and must also overload operator>>= for binding purposes.
    //monad must provide a specialization for the Monad class or a constructor which
    //takes a single parameter of the type of the valuse it's wrapping. As well as
    //that, It must also overload operator>>= for binding purposes.

    #pragma once

    template <template <typename a, typename...> class m>
    struct Monad
    {
    template <typename a> static m<a> unit(a value) { return m<a>::unit(value); }
    template <typename a> static m<a> unit(a value) { return m<a>(value); }
    };
  4. dpwright revised this gist Sep 9, 2013. 1 changed file with 0 additions and 5 deletions.
    5 changes: 0 additions & 5 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -8,13 +8,8 @@

    #pragma once

    #include <sstream>
    #include <string>

    #include <cassert>

    #include <functional>

    template<typename a> class Maybe
    {
    public:
  5. dpwright revised this gist Sep 9, 2013. 4 changed files with 12 additions and 14 deletions.
    12 changes: 5 additions & 7 deletions listm.h
    Original file line number Diff line number Diff line change
    @@ -11,20 +11,18 @@

    #include "monad.h"

    using namespace std;

    template<> struct Monad<list>
    template<> struct Monad<std::list>
    {
    template<typename a> static const list<a> unit (a value)
    template<typename a> static const std::list<a> unit (a value)
    {
    return list<a>(1, value);
    return std::list<a>(1, value);
    }
    };

    template<typename a, typename b>
    auto operator>>=(const list<a>& in, const b&& f) -> decltype(f(in.front()))
    auto operator>>=(const std::list<a>& in, const b&& f) -> decltype(f(in.front()))
    {
    typedef typename remove_const<decltype(f(in.front()))>::type tmpList;
    typedef typename std::remove_const<decltype(f(in.front()))>::type tmpList;
    tmpList out;
    for(auto i = in.begin(); i != in.end(); ++i)
    {
    2 changes: 0 additions & 2 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,6 @@

    #include <functional>

    using namespace std;

    template<typename a> class Maybe
    {
    public:
    2 changes: 2 additions & 0 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,8 @@

    #include "show.h"

    using namespace std;

    template<typename a> const Maybe<a> imSquare(a x) {
    return (abs(x) >= 0x8000) ? Maybe<a>::Nothing() : Maybe<a>::Just(x * x);
    }
    10 changes: 5 additions & 5 deletions show.h
    Original file line number Diff line number Diff line change
    @@ -16,22 +16,22 @@ template<typename t> std::string show(t x)
    template<typename a> std::string show(Maybe<a> x)
    {
    if(x.isJust()) {
    stringstream out;
    std::stringstream out;
    out << "Just " << x.fromJust();
    return out.str();
    } else return "Nothing";
    }

    template<typename a, typename b> std::string show(pair<a, b> x)
    template<typename a, typename b> std::string show(std::pair<a, b> x)
    {
    stringstream out;
    std::stringstream out;
    out << "(" << x.first << "," << x.second << ")";
    return out.str();
    }

    template<typename a> std::string show(list<a> x)
    template<typename a> std::string show(std::list<a> x)
    {
    stringstream out;
    std::stringstream out;
    out << "[";
    for(auto i = x.begin(); i != x.end(); ++i)
    {
  6. dpwright revised this gist Sep 9, 2013. 4 changed files with 32 additions and 39 deletions.
    23 changes: 0 additions & 23 deletions listm.h
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,6 @@
    #include <list>
    #include <type_traits>

    #include "show.h"
    #include "monad.h"

    using namespace std;
    @@ -34,25 +33,3 @@ auto operator>>=(const list<a>& in, const b&& f) -> decltype(f(in.front()))
    }
    return out;
    }

    template<typename a, typename b> std::string show(pair<a, b> x)
    {
    stringstream out;
    out << "(" << x.first << "," << x.second << ")";
    return out.str();
    }

    template<typename a> std::string show(list<a> x)
    {
    stringstream out;
    out << "[";
    for(auto i = x.begin(); i != x.end(); ++i)
    {
    out << show(*i);
    if(i != --x.end())
    out << ",";
    }
    out << "]";
    return out.str();
    }

    16 changes: 0 additions & 16 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,6 @@

    #include <functional>

    #include "show.h"

    using namespace std;

    template<typename a> class Maybe
    @@ -31,15 +29,6 @@ template<typename a> class Maybe

    static const Maybe<a> unit(a x) { return Maybe(x); }

    std::string show() const
    {
    if(isJust()) {
    stringstream out;
    out << "Just " << fromJust();
    return out.str();
    } else return "Nothing";
    }

    private:
    Maybe() : m_valid(false) {}
    Maybe(const a value) : m_value(value), m_valid(true) {}
    @@ -63,8 +52,3 @@ auto operator>>=(const Maybe<a>&& in, const b&& f) -> decltype(f(in.fromJust()))
    {
    return in.isJust() ? f(in.fromJust()) : Nothing<decltype(f(in.fromJust()))>();
    }

    template<typename a> std::string show(Maybe<a> x)
    {
    return x.show();
    }
    2 changes: 2 additions & 0 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,8 @@
    #include "maybem.h"
    #include "listm.h"

    #include "show.h"

    template<typename a> const Maybe<a> imSquare(a x) {
    return (abs(x) >= 0x8000) ? Maybe<a>::Nothing() : Maybe<a>::Just(x * x);
    }
    30 changes: 30 additions & 0 deletions show.h
    Original file line number Diff line number Diff line change
    @@ -12,3 +12,33 @@ template<typename t> std::string show(t x)
    out << x;
    return out.str();
    }

    template<typename a> std::string show(Maybe<a> x)
    {
    if(x.isJust()) {
    stringstream out;
    out << "Just " << x.fromJust();
    return out.str();
    } else return "Nothing";
    }

    template<typename a, typename b> std::string show(pair<a, b> x)
    {
    stringstream out;
    out << "(" << x.first << "," << x.second << ")";
    return out.str();
    }

    template<typename a> std::string show(list<a> x)
    {
    stringstream out;
    out << "[";
    for(auto i = x.begin(); i != x.end(); ++i)
    {
    out << show(*i);
    if(i != --x.end())
    out << ",";
    }
    out << "]";
    return out.str();
    }
  7. dpwright revised this gist Sep 9, 2013. 4 changed files with 102 additions and 7 deletions.
    13 changes: 7 additions & 6 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,10 @@
    # The Maybe monad in C++
    # Monadic operations in C++

    A further attempt to implement the Maybe monad, and a framework for writing
    monads in general, in C++. This is really for my own amusement rather than to
    try and do anything useful with them. It also gave me an excuse to try out C++
    11 lambda functions for the first time.
    This began as a further attempt to implement the Maybe monad in C++, but quickly
    spiralled out of control and now includes an implementation of the List monad as
    well (using std::list!). This is really for my own amusement rather than to try
    and do anything useful with them. It also gave me an excuse to try out C++ 11
    lambda functions for the first time.

    My original implementation defined a macro called `MBind` which caused a number
    of problems -- thankfully [PJayB][pjayb] managed to find a way around that so
    @@ -18,6 +19,6 @@ syntax on. In Haskell you'd probably use `do` syntax for a case like this, but
    I stuck with manually binding lambda functions so the similarity with the C++ is
    more obvious.

    Next up... The State and List monads!
    Next up... The State monad!

    [pjayb]: https://gist.github.com/PJayB
    58 changes: 58 additions & 0 deletions listm.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    //listm.h
    //
    //The List monad.
    //Provide a way to build up lists over a sequence of operations. Adds monadic
    //functionality to std::list

    #pragma once

    #include <list>
    #include <type_traits>

    #include "show.h"
    #include "monad.h"

    using namespace std;

    template<> struct Monad<list>
    {
    template<typename a> static const list<a> unit (a value)
    {
    return list<a>(1, value);
    }
    };

    template<typename a, typename b>
    auto operator>>=(const list<a>& in, const b&& f) -> decltype(f(in.front()))
    {
    typedef typename remove_const<decltype(f(in.front()))>::type tmpList;
    tmpList out;
    for(auto i = in.begin(); i != in.end(); ++i)
    {
    tmpList current = f(*i);
    out.splice(out.end(), current);
    }
    return out;
    }

    template<typename a, typename b> std::string show(pair<a, b> x)
    {
    stringstream out;
    out << "(" << x.first << "," << x.second << ")";
    return out.str();
    }

    template<typename a> std::string show(list<a> x)
    {
    stringstream out;
    out << "[";
    for(auto i = x.begin(); i != x.end(); ++i)
    {
    out << show(*i);
    if(i != --x.end())
    out << ",";
    }
    out << "]";
    return out.str();
    }

    26 changes: 26 additions & 0 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@

    #include "monad.h"
    #include "maybem.h"
    #include "listm.h"

    template<typename a> const Maybe<a> imSquare(a x) {
    return (abs(x) >= 0x8000) ? Maybe<a>::Nothing() : Maybe<a>::Just(x * x);
    @@ -17,9 +18,34 @@ const Maybe<int> testMaybe()
    return imSquare(c); };};};
    }

    template<typename a, typename b>
    const list< pair<a, b> > cartesianProduct(const list<a> xs, const list<b> ys)
    {
    return xs >>= [&] (const a x) {
    return ys >>= [&] (const b y) {
    pair<a, b> product(x, y);
    return Monad<list>::unit(product); };};
    }

    const list< pair<int, int> > testList()
    {
    static const int LIST_SIZE=5;

    list<int> xs_list;
    for(int i = 1; i <= LIST_SIZE; ++i)
    xs_list.push_back(i);

    list<int> ys_list;
    for(int i = 1; i <= LIST_SIZE; ++i)
    ys_list.push_back(i*i);

    return cartesianProduct(xs_list, ys_list);
    }

    int main(int argc, char** argv)
    {
    cout << show(testMaybe()) << endl;
    cout << show(testList()) << endl;

    return 0;
    }
    12 changes: 11 additions & 1 deletion monads.hs
    Original file line number Diff line number Diff line change
    @@ -8,4 +8,14 @@ testMaybe =
    return 0.5 >>= \d ->
    imSquare c

    main = print testMaybe
    cartesianProduct xs ys =
    xs >>= \x ->
    ys >>= \y ->
    return (x, y)

    testList = cartesianProduct xs ys
    where xs = [1..5]
    ys = map sq xs
    sq x = x * x

    main = print testMaybe >> print testList
  8. dpwright revised this gist Sep 9, 2013. 2 changed files with 11 additions and 9 deletions.
    12 changes: 7 additions & 5 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,13 @@
    //monad.h
    //
    //Defines the two monad operations. Every class which wants to be treated as a
    //monad must provide a static function called "unit", and must also overload
    //operator>>= for binding purposes.
    //monad must either provide a static function called "unit" or a specialization
    //for the Monad class, and must also overload operator>>= for binding purposes.

    #pragma once

    template<template <typename a> class m, typename a> const m<a> MUnit(a value) {
    return m<a>::unit(value);
    }
    template <template <typename a, typename...> class m>
    struct Monad
    {
    template <typename a> static m<a> unit(a value) { return m<a>::unit(value); }
    };
    8 changes: 4 additions & 4 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -10,11 +10,11 @@ template<typename a> const Maybe<a> imSquare(a x) {

    const Maybe<int> testMaybe()
    {
    return MUnit<Maybe>(0x4000) >>= [&] (const int a) {
    return imSquare(a) >>= [&] (const int b) {
    return Monad<Maybe>::unit(0x4000) >>= [&] (const int a) {
    return imSquare(a) >>= [&] (const int b) {
    int c = b / a;
    return MUnit<Maybe>(0.5f) >>= [&] (const float d) {
    return imSquare(c); };};};
    return Monad<Maybe>::unit(0.5f) >>= [&] (const float d) {
    return imSquare(c); };};};
    }

    int main(int argc, char** argv)
  9. dpwright revised this gist Sep 9, 2013. 3 changed files with 22 additions and 1 deletion.
    7 changes: 7 additions & 0 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,8 @@

    #include <functional>

    #include "show.h"

    using namespace std;

    template<typename a> class Maybe
    @@ -61,3 +63,8 @@ auto operator>>=(const Maybe<a>&& in, const b&& f) -> decltype(f(in.fromJust()))
    {
    return in.isJust() ? f(in.fromJust()) : Nothing<decltype(f(in.fromJust()))>();
    }

    template<typename a> std::string show(Maybe<a> x)
    {
    return x.show();
    }
    2 changes: 1 addition & 1 deletion monads.cpp
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@ const Maybe<int> testMaybe()

    int main(int argc, char** argv)
    {
    cout << testMaybe().show() << endl;
    cout << show(testMaybe()) << endl;

    return 0;
    }
    14 changes: 14 additions & 0 deletions show.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    //show.h
    //
    //Defines the "show" operation. Defaults to using the stream operator.

    #pragma once

    #include <sstream>

    template<typename t> std::string show(t x)
    {
    std::stringstream out;
    out << x;
    return out.str();
    }
  10. dpwright revised this gist Sep 9, 2013. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -6,8 +6,6 @@

    #pragma once

    #include <functional>

    template<template <typename a> class m, typename a> const m<a> MUnit(a value) {
    return m<a>::unit(value);
    }
  11. dpwright revised this gist Sep 8, 2013. 1 changed file with 4 additions and 5 deletions.
    9 changes: 4 additions & 5 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -31,12 +31,11 @@ template<typename a> class Maybe

    std::string show() const
    {
    stringstream out;
    if(isJust())
    if(isJust()) {
    stringstream out;
    out << "Just " << fromJust();
    else
    out << "Nothing";
    return out.str();
    return out.str();
    } else return "Nothing";
    }

    private:
  12. dpwright revised this gist Sep 8, 2013. 3 changed files with 9 additions and 25 deletions.
    23 changes: 6 additions & 17 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -5,23 +5,10 @@ monads in general, in C++. This is really for my own amusement rather than to
    try and do anything useful with them. It also gave me an excuse to try out C++
    11 lambda functions for the first time.

    Issues with this version:

    * Portability -- it only compiles with clang++. g++ falls over trying to expand
    `MBind`, so you can get it compiling on g++ if you expand these yourself, but
    the syntax starts to look very painful then.
    * Verbosity -- C++ isn't as good at inferring types as Haskell, and in
    particular it can't dispatch based on the return type. The first parameter to
    `MBind` is a particular annoyance -- it always has to match the type of the
    final expression, which in turn has to match the type you're assigning to --
    `result` in the case of `testMaybe`. So that's a lot of repetitive typing.
    * Incidentally, all `MBind` is actually doing is performing a typecast from
    a C++ lambda to a `std::function`, so that it can embed return type
    information. If we could just pass the lambda into the `operator>>=`
    function as-is we wouldn't need the `MBind` macro at all, and could
    instead just use the lambda, which has a lot of advantages including
    cleaner syntax and control over what we should include in the closure.
    * I had to use a `#define` for `MBind` :-(
    My original implementation defined a macro called `MBind` which caused a number
    of problems -- thankfully [PJayB][pjayb] managed to find a way around that so
    this version uses C++ lambdas directly (take a look at the changelog if you want
    to see the changes made).

    Please bear in mind I made this purely out of curiosity -- I realise a lot of
    this is pathological in C++.
    @@ -32,3 +19,5 @@ I stuck with manually binding lambda functions so the similarity with the C++ is
    more obvious.

    Next up... The State and List monads!

    [pjayb]: https://gist.github.com/PJayB
    5 changes: 0 additions & 5 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -3,11 +3,6 @@
    //Defines the two monad operations. Every class which wants to be treated as a
    //monad must provide a static function called "unit", and must also overload
    //operator>>= for binding purposes.
    //
    //I would have liked not to have had to resort to a #define for MBind, but
    //typing it in manually is pretty painful. In this case, it is operator>>=
    //which performs the binding operation itself; MBind is just a shortcut to hide
    //away C++'s hideous lambda syntax.

    #pragma once

    6 changes: 3 additions & 3 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -10,10 +10,10 @@ template<typename a> const Maybe<a> imSquare(a x) {

    const Maybe<int> testMaybe()
    {
    return MUnit<Maybe>(0x4000) >>= [&] (const int a) -> const Maybe<int> {
    return imSquare(a) >>= [&] (const int b) -> const Maybe<int> {
    return MUnit<Maybe>(0x4000) >>= [&] (const int a) {
    return imSquare(a) >>= [&] (const int b) {
    int c = b / a;
    return MUnit<Maybe>(0.5f) >>= [&] (const float d) -> const Maybe<int> {
    return MUnit<Maybe>(0.5f) >>= [&] (const float d) {
    return imSquare(c); };};};
    }

  13. @PJayB PJayB revised this gist Sep 8, 2013. 3 changed files with 7 additions and 11 deletions.
    9 changes: 4 additions & 5 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -52,14 +52,13 @@ template<typename a> const Maybe<a> Just(const a value)
    return Maybe<a>::Just(value);
    }

    template<typename a> const Maybe<a> Nothing()
    template<typename a> a Nothing()
    {
    return Maybe<a>::Nothing();
    return a::Nothing();
    }

    template<typename a, typename b>
    const Maybe<b> operator>>=(const Maybe<a>&& in,
    function<const Maybe<b> (const a)> f)
    auto operator>>=(const Maybe<a>&& in, const b&& f) -> decltype(f(in.fromJust()))
    {
    return in.isJust() ? f(in.fromJust()) : Maybe<b>::Nothing();
    return in.isJust() ? f(in.fromJust()) : Nothing<decltype(f(in.fromJust()))>();
    }
    3 changes: 0 additions & 3 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -13,9 +13,6 @@

    #include <functional>

    #define MBind(m, in_type, param_name) \
    (function<const m (const in_type)>) [&] (const in_type param_name)

    template<template <typename a> class m, typename a> const m<a> MUnit(a value) {
    return m<a>::unit(value);
    }
    6 changes: 3 additions & 3 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -10,10 +10,10 @@ template<typename a> const Maybe<a> imSquare(a x) {

    const Maybe<int> testMaybe()
    {
    return MUnit<Maybe>(0x4000) >>= MBind(Maybe<int>, int, a) {
    return imSquare(a) >>= MBind(Maybe<int>, int, b) {
    return MUnit<Maybe>(0x4000) >>= [&] (const int a) -> const Maybe<int> {
    return imSquare(a) >>= [&] (const int b) -> const Maybe<int> {
    int c = b / a;
    return MUnit<Maybe>(0.5f) >>= MBind(Maybe<int>, float, d) {
    return MUnit<Maybe>(0.5f) >>= [&] (const float d) -> const Maybe<int> {
    return imSquare(c); };};};
    }

  14. dpwright created this gist Sep 8, 2013.
    34 changes: 34 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    # The Maybe monad in C++

    A further attempt to implement the Maybe monad, and a framework for writing
    monads in general, in C++. This is really for my own amusement rather than to
    try and do anything useful with them. It also gave me an excuse to try out C++
    11 lambda functions for the first time.

    Issues with this version:

    * Portability -- it only compiles with clang++. g++ falls over trying to expand
    `MBind`, so you can get it compiling on g++ if you expand these yourself, but
    the syntax starts to look very painful then.
    * Verbosity -- C++ isn't as good at inferring types as Haskell, and in
    particular it can't dispatch based on the return type. The first parameter to
    `MBind` is a particular annoyance -- it always has to match the type of the
    final expression, which in turn has to match the type you're assigning to --
    `result` in the case of `testMaybe`. So that's a lot of repetitive typing.
    * Incidentally, all `MBind` is actually doing is performing a typecast from
    a C++ lambda to a `std::function`, so that it can embed return type
    information. If we could just pass the lambda into the `operator>>=`
    function as-is we wouldn't need the `MBind` macro at all, and could
    instead just use the lambda, which has a lot of advantages including
    cleaner syntax and control over what we should include in the closure.
    * I had to use a `#define` for `MBind` :-(

    Please bear in mind I made this purely out of curiosity -- I realise a lot of
    this is pathological in C++.

    I've also included the equivalent Haskell code, so you can see what I based the
    syntax on. In Haskell you'd probably use `do` syntax for a case like this, but
    I stuck with manually binding lambda functions so the similarity with the C++ is
    more obvious.

    Next up... The State and List monads!
    65 changes: 65 additions & 0 deletions maybem.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    //maybem.h
    //
    //The Maybe monad.
    //Provides a safer way to return possible failure than NULL values. Of course,
    //since C++ doesn't have pattern matching, it is down to you to ensure that you
    //always call isJust() to confirm there is a value available before calling
    //fromJust() to retrieve it.

    #pragma once

    #include <sstream>
    #include <string>

    #include <cassert>

    #include <functional>

    using namespace std;

    template<typename a> class Maybe
    {
    public:
    static const Maybe<a> Just(const a value) { return Maybe(value); }
    static const Maybe<a> Nothing() { return Maybe(); }

    const bool isJust() const { return m_valid; }
    const a fromJust() const { assert(isJust());
    return m_value; }

    static const Maybe<a> unit(a x) { return Maybe(x); }

    std::string show() const
    {
    stringstream out;
    if(isJust())
    out << "Just " << fromJust();
    else
    out << "Nothing";
    return out.str();
    }

    private:
    Maybe() : m_valid(false) {}
    Maybe(const a value) : m_value(value), m_valid(true) {}
    Maybe(const a value, const bool valid) : m_value(value), m_valid(valid) {}
    a m_value;
    const bool m_valid;
    };

    template<typename a> const Maybe<a> Just(const a value)
    {
    return Maybe<a>::Just(value);
    }

    template<typename a> const Maybe<a> Nothing()
    {
    return Maybe<a>::Nothing();
    }

    template<typename a, typename b>
    const Maybe<b> operator>>=(const Maybe<a>&& in,
    function<const Maybe<b> (const a)> f)
    {
    return in.isJust() ? f(in.fromJust()) : Maybe<b>::Nothing();
    }
    21 changes: 21 additions & 0 deletions monad.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    //monad.h
    //
    //Defines the two monad operations. Every class which wants to be treated as a
    //monad must provide a static function called "unit", and must also overload
    //operator>>= for binding purposes.
    //
    //I would have liked not to have had to resort to a #define for MBind, but
    //typing it in manually is pretty painful. In this case, it is operator>>=
    //which performs the binding operation itself; MBind is just a shortcut to hide
    //away C++'s hideous lambda syntax.

    #pragma once

    #include <functional>

    #define MBind(m, in_type, param_name) \
    (function<const m (const in_type)>) [&] (const in_type param_name)

    template<template <typename a> class m, typename a> const m<a> MUnit(a value) {
    return m<a>::unit(value);
    }
    25 changes: 25 additions & 0 deletions monads.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    #include <iostream>
    #include <cmath>

    #include "monad.h"
    #include "maybem.h"

    template<typename a> const Maybe<a> imSquare(a x) {
    return (abs(x) >= 0x8000) ? Maybe<a>::Nothing() : Maybe<a>::Just(x * x);
    }

    const Maybe<int> testMaybe()
    {
    return MUnit<Maybe>(0x4000) >>= MBind(Maybe<int>, int, a) {
    return imSquare(a) >>= MBind(Maybe<int>, int, b) {
    int c = b / a;
    return MUnit<Maybe>(0.5f) >>= MBind(Maybe<int>, float, d) {
    return imSquare(c); };};};
    }

    int main(int argc, char** argv)
    {
    cout << testMaybe().show() << endl;

    return 0;
    }
    11 changes: 11 additions & 0 deletions monads.hs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    imSquare x | abs x >= 0x8000 = Nothing
    | otherwise = Just (x * x)

    testMaybe =
    return 0x4000 >>= \a ->
    imSquare a >>= \b ->
    let c = b `div` a in
    return 0.5 >>= \d ->
    imSquare c

    main = print testMaybe