Help us understand the problem. What is going on with this article?

PHPでカリー化、部分適用、関数合成、そしてMaybeモナドなど

More than 5 years have passed since last update.

最初はMaybeモナドだけ作ろうと思っていたところ、fmapも欲しいし、そうするとFunctorかなー、fmapがあるなら <$> <*> もあった方がいいかなー、それだとFunctor => Applicative => Monad => Maybe にした方がいいなー、とか考えてこうなった。

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の実装は色々あるけれど、たぶん重要なのは引数の順番と部分適用。

$result1 = $maybeValue1->maybe($default, $callback);
$result2 = $maybeValue2->maybe($default, $callback);
$result3 = $maybeValue3->maybe($default, $customCallback);

これでもいいんだけど

$withDefault = maybe($default);
$withCallback = $withDefault($callback);

$result1 = $withCallback($maybeValue1);
$result2 = $withCallback($maybeValue2);
$result3 = $withDefault($customCallback, $maybeValue3);

が本来の使い方じゃないだろうか。

そんなわけで部分適用 や fmap を実装したモナドを書いた。
https://github.com/nishimura/laiz-monad

部分適用する場合、型推論がないので型をコメントに書くのは必須かもしれない。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away