Posted by Andrea Faulds

Andrea Faulds
[PHP-DEV] Concept: built-in functions for common functional primitives (composition, partial application, etc.) September 21, 2017 09:20PM |

Hi everyone,

Something I've long wanted in PHP has been built-in versions of some

common functional primitives, functions that operate on functions and

create new functions for you. I've finally gotten round to bringing this

up because my operator functions RFC could really benefit from them.

To be specific in what I mean by “functional primitives”, here are some

examples:

Function composition: a function that takes a function f($x) and a

function g($y) and returns a new function($x) { return g(f($x)); }. Thus

compose("trim", "strtoupper") returns a function that strips whitespace

from and capitalises a string. In practice it would make sense if f()

could any number of arguments.

Partial application: a function that takes a function and a set of

arguments, and returns a new function that calls that function with

those arguments, but also with any additional arguments passed. Thus, to

take an example from my operator functions RFC, papply("*", 2) returns a

function that multiplies a number by two. A question here is how to

decide the positions of the arguments: can I partially apply, say,

intdiv() and set the second argument, then have the resulting function

be called with intdiv()'s first argument? Maybe it could take an array

specifying argument indicies, so papply("/", [1 => 2]) returns a

function that divides *by* two.

Currying: a function that takes a function and returns a new function

that takes the function's first parameter and returns another function

to take its second, which returns another function, and so on, until

eventually all the function's arguments have been taken and the

function's result is returned. So, curry("intdiv")(6)(2) is equivalent

to intdiv(6, 2), and curry("intdiv")(6) is equivalent to

papply("intdiv", 6).

Reversing the order of the arguments: a function that takes a function

and returns a new function that calls the function with whatever

arguments it is passed, but in reverse order. So, reverse("intdiv")(4,

2) behaves like intdiv(2, 4).

The identity function: returns its argument. This function essentially

does nothing, but that's exactly the point, it can go in place of a

callback that could do something more sophisticated.

Constant function: a function that takes a value and returns a function

that returns that value. Like the identity function, the function this

returns is useless when called directly, but can have some use as a

callback.

This might not be an exhaustive list. There might be some other

functions of this kind that make sense, and suggestions would be

appreciated. Note that PHP has some functional primitives already

(array_map, array_reduce), so in a sense I'm just looking for what it's

missing.

If these functions were to be added, most of them would make sense as

both global functions and methods on Closure (think mysqli_* vs

$mysqli->*). The former makes sense when using callables which aren't

(neccessarily) closures, e.g. when composing built-in PHP functions

together. Of course, they could be just methods on Closure and have no

global function counterparts, but I think compose("trim", "strtoupper")

is much more appealing in practice than

Closure::fromCallable("trim")->compose(Closure::fromCallable("strtoupper")).

At that point you might as well just write the composition yourself as a

closure.

I'd like to acknowledge that of course almost all of these can be easily

written in plain PHP as userland functions, and indeed have been.

However, if PHP includes common operations like these in its standard

library, it increases the base language's expressive power. It also

means we can provide faster and edge-case-complete versions. For

example, while a basic implementation of function composition is five

lines or so of PHP code, a version that handles reflection and

references correctly is significantly longer and slower. If PHP has this

as a built-in function, it can be implemented more efficiently. This is

particularly important given this kind of higher-order function is often

used with operations like array_map() which have a multiplicative effect

that can make their speed significant.

Please tell me your thoughts on this idea.

Thanks!

--

Andrea Faulds

https://ajf.me/

--

PHP Internals - PHP Runtime Development Mailing List

To unsubscribe, visit: http://www.php.net/unsub.php

Something I've long wanted in PHP has been built-in versions of some

common functional primitives, functions that operate on functions and

create new functions for you. I've finally gotten round to bringing this

up because my operator functions RFC could really benefit from them.

To be specific in what I mean by “functional primitives”, here are some

examples:

Function composition: a function that takes a function f($x) and a

function g($y) and returns a new function($x) { return g(f($x)); }. Thus

compose("trim", "strtoupper") returns a function that strips whitespace

from and capitalises a string. In practice it would make sense if f()

could any number of arguments.

Partial application: a function that takes a function and a set of

arguments, and returns a new function that calls that function with

those arguments, but also with any additional arguments passed. Thus, to

take an example from my operator functions RFC, papply("*", 2) returns a

function that multiplies a number by two. A question here is how to

decide the positions of the arguments: can I partially apply, say,

intdiv() and set the second argument, then have the resulting function

be called with intdiv()'s first argument? Maybe it could take an array

specifying argument indicies, so papply("/", [1 => 2]) returns a

function that divides *by* two.

Currying: a function that takes a function and returns a new function

that takes the function's first parameter and returns another function

to take its second, which returns another function, and so on, until

eventually all the function's arguments have been taken and the

function's result is returned. So, curry("intdiv")(6)(2) is equivalent

to intdiv(6, 2), and curry("intdiv")(6) is equivalent to

papply("intdiv", 6).

Reversing the order of the arguments: a function that takes a function

and returns a new function that calls the function with whatever

arguments it is passed, but in reverse order. So, reverse("intdiv")(4,

2) behaves like intdiv(2, 4).

The identity function: returns its argument. This function essentially

does nothing, but that's exactly the point, it can go in place of a

callback that could do something more sophisticated.

Constant function: a function that takes a value and returns a function

that returns that value. Like the identity function, the function this

returns is useless when called directly, but can have some use as a

callback.

This might not be an exhaustive list. There might be some other

functions of this kind that make sense, and suggestions would be

appreciated. Note that PHP has some functional primitives already

(array_map, array_reduce), so in a sense I'm just looking for what it's

missing.

If these functions were to be added, most of them would make sense as

both global functions and methods on Closure (think mysqli_* vs

$mysqli->*). The former makes sense when using callables which aren't

(neccessarily) closures, e.g. when composing built-in PHP functions

together. Of course, they could be just methods on Closure and have no

global function counterparts, but I think compose("trim", "strtoupper")

is much more appealing in practice than

Closure::fromCallable("trim")->compose(Closure::fromCallable("strtoupper")).

At that point you might as well just write the composition yourself as a

closure.

I'd like to acknowledge that of course almost all of these can be easily

written in plain PHP as userland functions, and indeed have been.

However, if PHP includes common operations like these in its standard

library, it increases the base language's expressive power. It also

means we can provide faster and edge-case-complete versions. For

example, while a basic implementation of function composition is five

lines or so of PHP code, a version that handles reflection and

references correctly is significantly longer and slower. If PHP has this

as a built-in function, it can be implemented more efficiently. This is

particularly important given this kind of higher-order function is often

used with operations like array_map() which have a multiplicative effect

that can make their speed significant.

Please tell me your thoughts on this idea.

Thanks!

--

Andrea Faulds

https://ajf.me/

--

PHP Internals - PHP Runtime Development Mailing List

To unsubscribe, visit: http://www.php.net/unsub.php

Sorry, only registered users may post in this forum.