• Source
    1. #include <functional>
    2. #include <memory>
    3. #include <string>
    4. #include <iostream>
    5.  
    6. using std::placeholders::_1;
    7.  
    8. //
    9. // Maybe data type definition
    10. //
    11.  
    12. template <typename a>
    13. struct Maybe
    14. {
    15. std::shared_ptr<a> value;
    16.  
    17. // Evaluate the monadic action and yield its value
    18. const a& operator() () const
    19. {
    20. return *value;
    21. }
    22. };
    23.  
    24. // Construct a Nothing value, denoting failure
    25. template <typename a>
    26. Maybe<a> Nothing ()
    27. {
    28. return Maybe<a>(); // null pointer, empty value / failed computation
    29. }
    30.  
    31. // Construct a Just value, denoting a successful computation
    32. template <typename a>
    33. Maybe<a> Just (const a& x)
    34. {
    35. Maybe<a> maybe;
    36. maybe.value.reset(new a(x));
    37. return maybe;
    38. }
    39.  
    40. // Return true if the Maybe value is Nothing, false if it is Just a value.
    41. template <typename a>
    42. bool is_nothing (const Maybe<a>& x)
    43. {
    44. return x.value.get() == nullptr;
    45. }
    46.  
    47. //
    48. // Monad operations
    49. //
    50.  
    51. template <typename a>
    52. Maybe<a> ret (const a& x) // I am calling the function `ret` because `return` is a special keyword in C++
    53. {
    54. return Just(x);
    55. }
    56.  
    57. template <typename a, typename b>
    58. Maybe<b> mbind (const Maybe<a>& x, const std::function<Maybe<b>(const a&)>& f)
    59. {
    60. return is_nothing(x) ? Nothing<b>() : f(x());
    61. }
    62.  
    63. //
    64. // Example program
    65. //
    66.  
    67. // g++ type deduction is failing when passing functions directly to mbind,
    68. // so we wrap them in a MaybeF<int> to solve this issue
    69. template <typename T>
    70. using MaybeF = std::function<Maybe<int>(const int&)>;
    71.  
    72. Maybe<int> divide (int x, int y)
    73. {
    74. if (y == 0)
    75. return Nothing<int>();
    76. else
    77. return Just(x/y);
    78. }
    79.  
    80. Maybe<int> inc (int x)
    81. {
    82. return Just(x+1);
    83. }
    84.  
    85. // Compute (1/x) + 1
    86. Maybe<int> compute (int x)
    87. {
    88. MaybeF<int> inc(::inc);
    89. std::function<Maybe<int>(const int&)> div_by_x = std::bind(divide, _1, x);
    90. return mbind(mbind(ret(1), div_by_x), inc);
    91. }
    92.  
    93. template <typename T>
    94. std::string show (const Maybe<T>& x)
    95. {
    96. if (is_nothing(x)) return "Nothing";
    97. else return std::string("Just ") + std::to_string(x());
    98. }
    99.  
    100. int main ()
    101. {
    102. MaybeF<int> inc(::inc);
    103.  
    104. Maybe<int> x;
    105. Maybe<int> y = Just(3);
    106.  
    107. std::cout << "x: " << show(x) << std::endl;
    108. std::cout << "y: " << show(y) << std::endl;
    109.  
    110. Maybe<int> a = mbind(ret(2), inc);
    111. std::cout << "2 + 1 = " << show(a) << std::endl;
    112.  
    113. Maybe<int> b = mbind(mbind(ret(2), inc), inc);
    114. std::cout << "2 + 1 + 1 = " << show(b) << std::endl;
    115.  
    116. std::function<Maybe<int>(const int&)> div2 = std::bind(divide, _1, 2);
    117. Maybe<int> c = mbind(ret(4), div2);
    118. std::cout << "4/2 = " << show(c) << std::endl;
    119.  
    120. Maybe<int> d = compute(1);
    121. std::cout << "1/1 + 1 = " << show(d) << std::endl;
    122.  
    123. Maybe<int> e = compute(0);
    124. std::cout << "1/0 + 1 = " << show(e) << std::endl;
    125.  
    126. return 0;
    127. }