Вдохновившись статьей Еще раз о каррировании и частичном применении в PHP, в голову пришла реализация частичного применения метода, именно метода, а не функции.
Вводная часть
Изначально определение каррирования дается как преобразование функции от пары аргументов в функцию, берущую свои аргументы по одному. Это преобразование было введено М. Шейнфинкелем и Г. Фреге и получило свое название в честь Х. Карри. Давайте теперь распространим это определение и на метод. Реализация этой идеи проста как 2 байта, но дает огромный потенциал. В этом и заключается доля метапрограммирования, когда методы можно создать в так сказать run-time, причем явно не описывая тело метода.
Исходный код
И так вот он класс с каррирующим методом, который и реализует (простите за каламбур) частичное использование метода класса. При этом, создается псевдометод, который и вызывается (опять с каламбурил) магическим методом __call():
abstract class ACurry
{
/**
* A curry method that returns a partial call of function
* or a result of its execution, depending on the number
* of parameters of the invoked method
*
* @param array $callback
* @param array $args
* @return callable
*/
protected function curry($callback, $args = array())
{
return function() use($callback, $args)
{
$methodInfo = new ReflectionMethod(get_class($callback[0]), $callback[1]);
if (count(array_merge($args, func_get_args())) >= $methodInfo->getNumberOfParameters()) {
return call_user_func_array($callback, $args);
} else {
return $callback[0]->curry($callback, $args);
}
};
}
/**
* Create a method $methodName by currying a method of $curryMethodName
* with arguments $args
*
* @param string $methodName
* @param string $curryMethodName
* @param array $args
* @return ACurry
*/
public function createMethod($methodName, $curryMethodName, $args = array())
{
$this->$methodName = $this->curry(array($this, $curryMethodName), $args);
return $this;
}
/**
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, $args)
{
if (property_exists($this, $name) && is_callable($this->$name)) {
return call_user_func_array($this->$name, $args);
}
}
}
Пример
Вот мой пример применения, он сделан по аналогии с примером товарища Bodigrim
<?php
require_once 'ACurry.php';
/**
* A class to calculate a mass from the density and size
*/
class Masses extends ACurry{
public function __construct(){
/* create method to calculate mass of iron cube */
$this->createMethod('ironCube', 'cube', array(7.8));
}
/**
* Method return a mass of subjection from density and size
*/
public function get($density, $length, $width, $height){
return $density * $length * $width * $height;
}
/**
* Method return a mass of cube subjection from density and size
*/
public function cube($density, $length){
return $this->get($density, $length, $length, $length);
}
}
$masses=new Masses();
echo $masses->ironCube(2);
В данном примере псевдометод ironCube() вычисляет массу железного куба в граммах со стороной 2 см (как известно, у железа плотность 7.8 г/см³).
Итог
Вот и получилось этакое объектно-ориентированное функциональное метапрограммирование. Конечно же, область применимости этого приема возрастет в разы, если преобразуем класс в trait, что позволит нам каррировать уже наследованные методы.
Автор: 3axap4eHko