├── LICENSE ├── README.md └── src ├── example.php └── partial.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Marcelo Camargo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Partial function application in PHP 2 | 3 | #### Introduction 4 | 5 | I'm a functional programming shiite, and I love dealing with functions. In languages like LiveScript or Haskell, we can make 6 | a partial application of a function, this is, if a function doesn't receive the expected amount of parameters, we can 7 | return another function to receive them. This is very useful when dealing with pure functions and data handling. In LiveScript, 8 | we can do things such as: 9 | 10 | ```livescript 11 | add = (x, y, z) --> x + y + z 12 | add # λ -> x + y + z 13 | add 10 # λ -> 10 + y + z 14 | add 10, 20 # λ -> 10 + 20 + z 15 | add 10, 20, 30 # 10 + 20 + 30 -> 60 16 | ``` 17 | 18 | Realize that we return functions when the number of parameters doesn't satisfy the required number, therefore, we can build 19 | other functions over these, such as `add_10 = add 10`, and reuse it without modifiying the core of `add`! 20 | 21 | All the implementations I saw in PHP were incomplete and with static arity, unable to partialize an already existing function, 22 | therefore, I took 1 hour to make a LiveScript compatible version! 23 | 24 | #### Currying vs Partial Application 25 | 26 | We need to differentiate *currying* from *partial application*. According to Wikipedia: 27 | 28 | > Currying is the technique of transforming a function that takes multiple arguments in such a way that it can be 29 | > called as a chain of functions each with a single argument. 30 | 31 | Currying, per se, may not be as useful as partial application, at least in non-pure languages, like PHP. 32 | 33 | Partial application may be defined as, according to Wikipedia: 34 | 35 | > In computer science, partial application (or partial function application) refers to the process of fixing a number 36 | > of arguments to a function, producing another function of smaller arity. 37 | 38 | That's exactly what we want! 39 | 40 | #### Examples 41 | 42 | ```php 43 | $add = partial(function($x, $y, $z) { 44 | return $x + $y + $z; 45 | }); 46 | 47 | $a = $add; 48 | $b = $add(10); 49 | $c = $add(10, 20); 50 | $d = $add(10, 20, 30); 51 | 52 | echo $a(10, 20, 30), PHP_EOL; 53 | echo $b(20, 30), PHP_EOL; 54 | echo $c(30), PHP_EOL; 55 | echo $d, PHP_EOL; 56 | ``` 57 | 58 | In all the results above we got 60 as output. You can also preinitialize a value while using `partial` and partialize 59 | your already existing functions. 60 | 61 | If, in functional programming, we can build more complex function using function composition, why can't we build more simpler 62 | functions by dividing them? 63 | 64 | We can, by example, build a function that doubles a list in a very easy way: 65 | 66 | ```php 67 | $double_list = partial('array_map', function($x) { 68 | return $x * 2; 69 | }); 70 | 71 | var_dump($double_list([1, 2, 3, 4, 5])); // [2, 4, 6, 8, 10] 72 | ``` 73 | 74 | #### Basic usage 75 | 76 | ```php 77 | require_once 'partial.php'; 78 | 79 | function add($x, $y, $z) { 80 | return $x + $y + $z; 81 | } 82 | 83 | $partial_add = partial('add'); 84 | // Enjoy! 85 | ``` 86 | 87 | #### How does it work? 88 | 89 | `partial` receives a function and an arbitrarily defined number of arguments. We apply reflection on the received 90 | function and check the number of arguments. We return a function that receives the rest of the arguments and, 91 | when we match the required number, we evaluate the function with the passed parameters. When not, we recursively 92 | call `partial` with the current arguments and the same function, until we reach the edge-case to evaluate it. 93 | 94 | Made with :heart: by Haskell Camargo 95 | -------------------------------------------------------------------------------- /src/example.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | function partial($fn) 28 | { 29 | // Fetch the initial parameters on initialization 30 | $start_parameters = array_slice(func_get_args(), 1); 31 | $required_size = (new \ReflectionFunction($fn))->getNumberOfRequiredParameters(); 32 | 33 | // When we have enough arguments to evaluate the function, the edge-case. 34 | if (sizeof($start_parameters) >= $required_size) { 35 | return call_user_func_array($fn, $start_parameters); 36 | } 37 | 38 | // When we must partialize it 39 | return function() use ($start_parameters, $required_size, $fn) { 40 | $rest_parameters = func_get_args(); 41 | $remaining_size = $required_size - (count($rest_parameters) + count($start_parameters)); 42 | 43 | // Join the current parameters with the newly received parameters 44 | $all_params = array_merge($start_parameters, $rest_parameters); 45 | 46 | // Append the function as the first item and call partialization again 47 | array_unshift($all_params, $fn); 48 | return call_user_func_array('partial', $all_params); 49 | }; 50 | } 51 | --------------------------------------------------------------------------------