PHP で合成関数を実現するライブラリは無いものか、と思って探したところ見つからなかったので、yuyat\compose というのを作ってみました。
が、いざ Packagist に submit しようとしたところ、「似たような名前のライブラリあるよ?」として igorw\compose をサジェストしてくれました。
思い切り車輪の再発明をしてしまいました...
ソースを眺めたところやっていることはほとんど同じですが、igorw\compose
は PHP 5.3 でも動くような実装になっていました。
せっかくなので、yuyat\compose
は PHP 5.6 の新機能の実験場にしてみることにしました。
yuyat\compose の実装
現時点で最新版の実装です。
ライブラリ本体としてはこれで全てです。
https://github.com/yuya-takeyama/compose/blob/186a04d91c6dd7db86c7683e96bb1f5fff536bab/src/compose.php
<?php
namespace yuyat;
function compose(callable ...$functions)
{
if (count($functions) < 2) {
throw new \InvalidArgumentException('at least two functions are required');
}
$initial = array_shift($functions);
return array_reduce($functions, function ($f, $g) {
return function (...$args) use ($f, $g) {
return $f($g(...$args));
};
}, $initial);
}
function pipeline(callable ...$functions)
{
return compose(...array_reverse($functions));
}
使い方
yuyat\compose
を使うと、以下のように、複数の関数を組み合わせて、スペース区切りの文字列を lowerCamelCase に変換するする関数を作ることができます。
<?php
use function yuyat\compose;
$splitAsWords = function ($str) {
return \preg_split('/\s+/u', $str);
};
$camelizeWords = function ($words) {
return \array_map('ucfirst', $words);
};
$join = function ($words) {
return \join('', $words);
};
$lowerCamelize = compose('lcfirst', $join, $camelizeWords, $splitAsWords);
echo $lowerCamelize('foo bar baz'); // => "fooBarBaz"
yuyat\pipeline
という関数もあって、こちらは合成した関数の適用順が yuyat\compose
の逆になっています。
コード上で読み下せる順番で適用されるので、こちらの方が読みやすいかもしれいません。
PHP 5.6 の新機能
yuyat\compose
のなかで使ったものを紹介します。
より詳細な情報については以下のページを参照してください。
Variadic functions via ... (可変長引数のための構文)
可変長引数自体は、これまでも func_get_args()
等を使って実現できましたが、専用の構文が用意されました。
以下のような関数を定義することで、$args
には引数の array
がセットされるようになります。
function f(...$args) {
}
また、タイプヒンティングにも対応しています。
以下の場合は、$args
内の全ての引数が callable
である必要があります。
function f(callable ...$args) {
}
これまでは可変長引数を使用した場合、型チェックは自前で実装する必要がありましたが、全ての型が同一であり、かつスカラ型でない、という前提においてはタイプヒンティングの恩恵を受けられるようになりました。
なお、HHVM でも Variadic functions には対応していますが、タイプヒンティングの併用をすることはできないようです。
全ての引数が同一の型である以上は、静的な解決ができそうな気はしますが、いずれ実装されるものなのかどうか気になるところではあります。
Argument unpacking via ... (引数展開のための構文)
Variadic functions の対になるような機能です。
以下のように関数呼び出しを行うと、
$args = [1, 2, 3];
f(...$args);
以下のように実行されるようになります。
f(1, 2, 3);
これまでは call_user_func_array
等を使って実現していましたが、より直感的な専用の構文が用意された形です。
また、展開される引数としては、array
だけでなく Traversable
インターフェイスを実装したオブジェクトも使用可能です。
use function (名前空間内関数のインポート)
これは README 中の Usage として使っていますが、名前空間内のクラスだけでなく、関数もインポートできるようになりました。
以下のように記述すると、
use function yuyat\compose;
yuyat\compose
を、単に compose
として呼び出すことができます。
また、以下のように別名を作ることも、クラスと同様にできます。
use function yuyat\compose as c;
まとめ
これまでの不可能を可能にしたような大きな変更ではないものの、着実に使いやすい言語に進化していると思いました。
ZendEngine 以外の処理系の追随も気になるところです。