fork download
  1. #include <functional>
  2. #include <utility>
  3. #include <iostream>
  4.  
  5. template<template<class ...> class M>
  6. struct Monad {
  7. // return :: a -> m a
  8. template<typename T>
  9. static auto ret(T a) -> M<T>;
  10.  
  11. // bind :: m a -> (a -> m b) -> m b
  12. template<typename T, typename U>
  13. static auto bind(M<T>, std::function<M<U>(T)>) -> M<U>;
  14. };
  15.  
  16. // return :: a -> m a
  17. template<template<class...> class M, typename T>
  18. auto ret(T a) -> M<T>
  19. {
  20. return Monad<M>::ret(std::move(a));
  21. }
  22.  
  23. // (>>=) :: m a -> (a -> m b) -> m b
  24. template<template<class...> class M, typename T, typename F>
  25. auto operator >>= (M<T> ma, F fun) -> decltype(fun(std::declval<T>())) // запутанно, но не знаю, как сделать лучше, чтобы работали лямбды
  26. {
  27. using R = decltype(fun(std::declval<T>()));
  28. return Monad<M>::bind(ma, std::function<R(T)>(std::move(fun)));
  29. }
  30.  
  31. // (>>) :: m a -> m b -> m b
  32. template<template<class...> class M, typename T, typename U>
  33. auto operator >> (M<T> ma, M<U> mb) -> M<U>
  34. {
  35. return ma >>= [=](T){ return mb; };
  36. }
  37.  
  38. // -----
  39.  
  40. struct real_world
  41. {
  42. explicit real_world(size_t i = 0) : m_i(i) {}
  43.  
  44. real_world(const real_world &) = delete;
  45. real_world(real_world && other) : m_i(other.m_i) {}
  46.  
  47. const real_world & operator = (const real_world & other) = delete;
  48. const real_world & operator = (real_world && other) = delete;
  49.  
  50. // "меняем мир"
  51. // принимаем функцию с побочными эффектами,
  52. // возвращаем пару из "нового мира" и результата функции
  53. template<typename T, typename F>
  54. std::pair<real_world, T> change(F f) const
  55. {
  56. return std::make_pair(real_world{m_i + 1}, f());
  57. }
  58.  
  59. private:
  60. const size_t m_i;
  61. };
  62.  
  63. template<typename T>
  64. struct IO {
  65. // инкапсулируем функцию, маскирующую побочные эффекты через возврат "нового мира"
  66. using f_type = std::function<std::pair<real_world, T>(real_world)>;
  67.  
  68. explicit IO(f_type f)
  69. : m_f(std::move(f))
  70. {
  71. }
  72.  
  73. // запускаем функцию, которая порождает "новый мир" и значение
  74. auto run(real_world w) const -> std::pair<real_world, T>
  75. {
  76. return m_f(std::move(w));
  77. }
  78.  
  79. private:
  80. f_type m_f;
  81. };
  82.  
  83. template<>
  84. template<typename T>
  85. auto Monad<IO>::ret(T a) -> IO<T>
  86. {
  87. // создаем IO с функцией, которая просто создает пару из "мира" и значения,
  88. // не производя побочных вычислений и не меняя мир
  89. return IO<T>([=](real_world w){return std::make_pair(std::move(w), a);});
  90. }
  91.  
  92. template<>
  93. template<typename T, typename U>
  94. auto Monad<IO>::bind(IO<T> ma, std::function<IO<U>(T)> f) -> IO<U>
  95. {
  96. // возвращаем IO с функцией, которая принимает "мир",
  97. // запускает на нем первое вычисление, получает "следующий мир" и новое значение,
  98. // затем вычисляем функцию от значения, получая новое IO вычисление,
  99. // которое и запускаем над "следующим миром"
  100. return IO<U>([=](real_world w) {
  101. auto world_and_data = ma.run(std::move(w));
  102. auto next_io = f(world_and_data.second);
  103. return next_io.run(std::move(world_and_data.first));
  104. });
  105. }
  106.  
  107. struct unit {};
  108.  
  109. auto putChar(char ch) -> IO<unit>
  110. {
  111. // Создаем IO с функцией, которая принимает "мир"
  112. // и выводит символ на экран, возвращая новый "мир"
  113. // (см. real_world::change)
  114. return IO<unit>([=](real_world w) {
  115. return w.change<unit>([=]{
  116. std::cout << ch;
  117. return unit{};
  118. });
  119. });
  120. };
  121.  
  122. auto getChar(unit) -> IO<char>
  123. {
  124. // Создаем IO с функцией, которая принимает "мир"
  125. // и читает символ, возвращая новый "мир" и прочитанный символ
  126. // (см. real_world::change)
  127. return IO<char>([=](real_world w) {
  128. return w.change<char>([=]{
  129. char res;
  130. if (!std::cin.get(res).good()) return '\0'; // пока так
  131. return res;
  132. });
  133. });
  134. }
  135.  
  136. // -----
  137.  
  138. auto putLine(std::string str) -> IO<unit>
  139. {
  140. // если строка пустая, то возвращаем действие ввода-вывода,
  141. // которое выводит символ '\n',
  142. // и останавливаем рекурсию
  143. if (str.empty()) {
  144. return putChar('\n');
  145. }
  146. // возвращаем действие ввода-вывода, которое сначала выводит первый символ строки,
  147. // затем рекурсивно выводит остаток строки
  148. return putChar(str[0]) >> putLine(str.substr(1));
  149. }
  150.  
  151. auto getLine(unit) -> IO<std::string>
  152. {
  153. // возвращаем действие ввода-вывода, которое считывает первый символ,
  154. // и если он означает конец строки,
  155. // то возвращает действие ввода-вывода, которое вернет пустую строку,
  156. // в противном же случае считываем(рекурсивно) строку и возвращаем действие ввода-вывода,
  157. // которое вернет сконкатенированную строку из принятого ранее символа и новой строки
  158. return getChar(unit{}) >>= [=](char ch) {
  159. if (ch == '\n' || ch == '\r' || ch == '\0') {
  160. return ret<IO>(std::string());
  161. }
  162. return getLine(unit{}) >>= [=](std::string str) {
  163. return ret<IO>(ch + str);
  164. };
  165. };
  166. }
  167.  
  168. int main()
  169. {
  170. auto io_main =
  171. putLine("What is your name?") >>
  172. getLine(unit{}) >>= [=](std::string name) {
  173. return putLine("Nice to meet you, " + name + "!");
  174. };
  175. io_main.run(real_world{});
  176. }
  177.  
Success #stdin #stdout 0s 3480KB
stdin
John
stdout
What is your name?
Nice to meet you, John!