1. nishimura

    Posted

    nishimura
Changes in title
+PHPでカリー化、部分適用、関数合成、そしてMaybeモナドなど
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,115 @@
+最初はMaybeモナドだけ作ろうと思っていたところ、fmapも欲しいし、そうするとFunctorかなー、fmapがあるなら <$> <*> もあった方がいいかなー、それだとFunctor => Applicative => Monad => Maybe にした方がいいなー、とか考えてこうなった。
+
+
+
+```php
+class Curry
+{
+ protected $value;
+
+ public function __construct(callable $f)
+ {
+ $this->value = self::curry($f);
+ }
+
+ public static function curry(callable $f)
+ {
+ $ref = new \ReflectionFunction($f);
+ $count = $ref->getNumberOfParameters();
+
+ if ($count === 0)
+ throw new \InvalidArgumentException('Cannot curry none arguments function');
+
+ $args = array_fill(0, $count, null);
+ $prev = function($a) use ($f, &$args, $count){
+ $args[$count - 1] = $a;
+ return call_user_func_array($f, $args);
+ };
+ for ($i = $count - 2; $i >= 0; $i--){
+ $prev = new static(function($a) use ($prev, &$args, $i){
+ $args[$i] = $a;
+ return $prev;
+ });
+ }
+ return $prev;
+ }
+
+ public function __invoke($a)
+ {
+ if ($this->value instanceof Curry){
+ $count = 1;
+ }else if (is_callable($this->value)){
+ $ref = new \ReflectionFunction($this->value);
+ $count = $ref->getNumberOfParameters();
+ }
+
+ $args = func_get_args();
+ $params = [];
+ for ($i = 0; $i < $count; $i ++){
+ $params[] = array_shift($args);
+ }
+ $ret = call_user_func_array($this->value, $params);
+
+ if ($args){
+ return call_user_func_array($ret, $args);
+ }else{
+ if (is_callable($ret) && !($ret instanceof Curry))
+ return new static($ret);
+ else
+ return $ret;
+ }
+ }
+
+ /*
+ * alias of __invoke
+ */
+ public function apply()
+ {
+ return call_user_func_array($this, func_get_args());
+ }
+
+ public function compose(callable $f)
+ {
+ return new static(function ($a) use ($f){
+ return $f(call_user_func_array($this, func_get_args()));
+ });
+ }
+}
+```
+
+
+一応検索して
+http://qiita.com/kumazo@github/items/3c337c6d51f023ae59ac
+http://qiita.com/yuya_takeyama/items/e14758907ca905386114
+この辺のカリー化のコードを見たんだけども、どうも関数実行時にうまいこと帳尻を合わせている感がある。これだとせっかくカリー化しても合成するときにうまくいかないんじゃないかな?と思って引数ひとつの関数に愚直に分解した。
+
+
+そしてモナド。
+OOPとしてMaybeの実装は色々あるけれど、たぶん重要なのは引数の順番と部分適用。
+
+```php
+$result1 = $maybeValue1->maybe($default, $callback);
+$result2 = $maybeValue2->maybe($default, $callback);
+$result3 = $maybeValue3->maybe($default, $customCallback);
+```
+
+これでもいいんだけど
+
+```php
+$withDefault = maybe($default);
+$withCallback = $withDefault($callback);
+
+$result1 = $withCallback($maybeValue1);
+$result2 = $withCallback($maybeValue2);
+$result3 = $withDefault($customCallback, $maybeValue3);
+```
+
+が本来の使い方じゃないだろうか。
+
+
+
+そんなわけで部分適用 や fmap を実装したモナドを書いた。
+https://github.com/nishimura/laiz-monad
+
+
+部分適用する場合、型推論がないので型をコメントに書くのは必須かもしれない。