📜 ⬆️ ⬇️

Object Oriented Functional Metaprogramming

The art of currying
Inspired by the article Once again on currying and partial application in PHP , I realized a partial application of the method, namely the method, not the function.

Prologue


Initially, the definition of currying is given as the transformation of a function from a pair of arguments to a function that takes its arguments one by one. This transformation was introduced by M. Scheinfinkel and G. Frege and received its name in honor of H. Curry. Let us now extend this definition to the method. The implementation of this idea is simple as 2 bytes, but it gives a huge potential. Therein lies the share of metaprogramming, when methods can be created in a run-time, so to speak, without explicitly describing the method body.

Source


And so here it is a class with a currying method that implements (excuse the pun) a partial use of the class method. At the same time, a pseudo method is created, which is called (again, by a pencil) with the __call () magic method:

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); } } } 

Example


Here is my example of application, it is made by analogy with the example of Comrade 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); 

In this example, the pseudo-method ironCube () calculates the mass of the iron cube in grams with a side of 2 cm (as is known, iron has a density of 7.8 g / cm ³).
')

Total


So it turned out sort of object-oriented functional metaprogramming. Of course, the range of applicability of this technique will increase by several times if we convert the class into a trait, which will allow us to curry methods already inherited.

Source: https://habr.com/ru/post/148899/


All Articles