Guest User

Untitled

a guest
Mar 19th, 2018
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.62 KB | None | 0 0
  1. <?php
  2. declare(strict_types=1);
  3.  
  4. // ((a, ...) -> z) -> a -> ... -> z
  5. function curry(callable $f, ...$args) {
  6. return function (...$partialArgs) use ($f, $args) {
  7. return (function ($args) use ($f) {
  8. return \count($args) < (new \ReflectionFunction($f))->getNumberOfRequiredParameters()
  9. ? curry($f, ...$args)
  10. : $f(...$args);
  11. })(\array_merge($args, $partialArgs));
  12. };
  13. }
  14.  
  15. interface Functor {
  16. // Functor F => (a -> b) -> F b
  17. public function map(callable $f)/*: Functor*/;
  18. }
  19.  
  20. // Functor F => (a -> b) -> F a -> F b
  21. function fmap(...$args) {
  22. return curry(function(callable $f, Functor $F) {
  23. return $F->map($f);
  24. })(...$args);
  25. }
  26.  
  27. interface Applicative extends Functor {
  28. // Applicative F => a -> F a
  29. public static function pure($x)/*: Applicative*/;
  30. // Applicative F => F a -> F b
  31. public function ap(Applicative $F)/*: Applicative*/;
  32. }
  33.  
  34. // Applicative A => A (a -> b) -> A a -> A b
  35. function ap(...$args) {
  36. return curry(function(Applicative $F1, Applicative $F2) {
  37. return $F1->ap($F2);
  38. })(...$args);
  39. }
  40.  
  41. // Applicative A => (a -> b -> c) -> A a -> A b -> A c
  42. function liftA2(...$args) {
  43. return curry(function (callable $f, Applicative $F1, Applicative $F2) {
  44. return $F1->map($f)->ap($F2);
  45. })(...$args);
  46. }
  47.  
  48. interface Monad extends Applicative {
  49. // Monad M => M (M a) -> M a
  50. public function join()/*: Monad*/;
  51. // Monad M => (a -> M b) -> M b
  52. public function bind(callable $f)/*: Monad*/;
  53. }
  54.  
  55. // Monad M => (a -> M b) -> M a -> M b
  56. function bind(...$args) {
  57. return curry(function (callable $f, Monad $M) {
  58. return $M->map($f)->join();
  59. })(...$args);
  60. }
  61.  
  62. // ====
  63.  
  64. class Maybe implements Functor, Applicative, Monad
  65. {
  66. /** @var bool */
  67. private $isJust;
  68.  
  69. /** @var mixed|null */
  70. private $value;
  71.  
  72. public static function just($value): Maybe
  73. {
  74. return new self(true, $value);
  75. }
  76.  
  77. public static function nothing(): Maybe
  78. {
  79. return new self(false);
  80. }
  81.  
  82. public function match(callable $just, callable $nothing)
  83. {
  84. return $this->isJust
  85. ? $just($this->value)
  86. : $nothing();
  87. }
  88.  
  89. public function map(callable $f): Maybe
  90. {
  91. return $this->isJust
  92. ? self::just($f($this->value))
  93. : self::nothing();
  94. }
  95.  
  96. public static function pure($value): Maybe
  97. {
  98. return self::just($value);
  99. }
  100.  
  101. public function ap(Applicative $F): Maybe
  102. {
  103. return $F->map($this->value);
  104. }
  105.  
  106. public function join(): Maybe
  107. {
  108. return $this->isJust
  109. ? $this->value
  110. : self::nothing();
  111. }
  112.  
  113. public function bind(callable $f): Maybe
  114. {
  115. return $this->map($f)->join();
  116. }
  117.  
  118. private function __construct(bool $isJust, $value = null)
  119. {
  120. $this->isJust = $isJust;
  121. $this->value = $value;
  122. }
  123. }
  124.  
  125. // a -> Maybe a -> a
  126. function fromMaybe(...$args) {
  127. return curry(function ($default, Maybe $a) {
  128. return $a->match(
  129. function ($value) { return $value; },
  130. function () use ($default) { return $default; }
  131. );
  132. })(...$args);
  133. }
  134.  
  135. // b -> (a -> b) -> Maybe a -> b
  136. function maybe(...$args) {
  137. return curry(function($default, callable $f, Maybe $a) {
  138. return $a->match(
  139. function ($value) use ($f) { return $f($value); },
  140. function () use ($default) { return $default; }
  141. );
  142. })(...$args);
  143. }
  144.  
  145. // ====
  146.  
  147. // example 1
  148. function odd(int $x): bool { return $x % 2 === 1; }
  149. assert(true === maybe('nope', 'odd', Maybe::just(1)));
  150. assert(false === maybe('nope', 'odd', Maybe::just(2)));
  151. assert('nope' === maybe('nope', 'odd', Maybe::nothing()));
  152.  
  153. // example 2
  154. function add(...$args) {
  155. return curry(function (int $x, int $y): int {
  156. return $x + $y;
  157. })(...$args);
  158. }
  159. assert(3 === fromMaybe('nope', liftA2('add', Maybe::just(1), Maybe::just(2))));
  160. assert('nope' === fromMaybe('nope', liftA2('add', Maybe::just(1), Maybe::nothing())));
  161.  
  162. // example 3
  163. function divByX(int $x): Maybe/*int*/ {
  164. return $x !== 0
  165. ? Maybe::just(10 / $x)
  166. : Maybe::nothing();
  167. }
  168. assert(5 === fromMaybe('nope', bind('divByX', Maybe::just(2))));
  169. assert('nope' === fromMaybe('nope', bind('divByX', Maybe::just(0))));
  170. assert('nope' === fromMaybe('nope', bind('divByX', Maybe::nothing())));
  171.  
  172. // example 4
  173. class X {
  174. private $a;
  175.  
  176. public function __construct(Maybe/*int*/ $a)
  177. {
  178. $this->a = $a;
  179. }
  180.  
  181. public function getA(): Maybe/*int*/
  182. {
  183. return $this->a;
  184. }
  185. }
  186. assert(1 === fromMaybe('nope', (new X(Maybe::just(1)))->getA()));
  187. assert('nope' === fromMaybe('nope', (new X(Maybe::nothing()))->getA()));
Add Comment
Please, Sign In to add comment