Using Reflection for Tailored Function Composition

An example with a re-implementation of array_map in PHP

Steven BOEHM
The Startup
3 min readAug 17, 2020

--

Elephant (PHP) Reflection illustration

Note: This article requires understanding of basic functional programming techniques. If you’re unfamiliar with that, you can read my previous article on functional programming in PHP !

The initial problem

There are plenty of reasons why one would want to re-implement the array_map function. Mine is that I want it to be curried and to behave a bit more like the JS Array#map method.

In the original version of array_map, if I wanted the keys of my array passed to the callback function, I'd have to pass the keys as additional (third) argument.

And if I want to use the array in the callback function, I’ll have to bring it in the closure with the use statement, which can lead to impure code if I pass the array by reference and modify it or if I'm iterating over a Traversable object.

So, what we want is an implementation of the map function that is easier to use and always pass our array by value. We’ll call this function _map :

That’s better, now we don’t have to pass the keys of our array as parameter to the map function and we have a copy of our array as third argument, let’s try that with a simple function strtoupper :

We expect $upperNames to contain the uppercase names (['BOB', 'ALICE', 'EVE']), but instead, here's what we got :

Warning :  strtoupper() expects exactly 1 parameter, 3 given

Of course, that’s not difficult to fix. We could come up with the following solution, just passing to the strtoupper function what it needs :

But that’s not very handy, and quite wordy also. We don’t want to create a new closure each time we want to pass less than 3 parameters to our mapping function.

Instead we’d rather pass to our mapping function exactly the number of arguments it expects and that’s where Reflection come into play !

What is Reflection and how can it help ?

Let’s start with a simple Wikipedia quote :

In computer science, reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.

What it means for us is that we can have some information about our variables / functions / classes at runtime. For instance, we can have a list of methods from a class, or a function’s parameters :

Here, we’ve learned (at runtime) that the implode function takes 2 parameters : one named "glue" and another one named "pieces".

You may now see how reflection can help us solve our problem ! Reflection allows us to know how many parameters a function expects, in other words, we can determine a function’s arity !

And if we know our function’s arity, then we can give it exactly the number of arguments it expects ! OK, let’s put all of that into code :

Real world example

Consider the following JSON :

It represents a list of travels, each one with different travel items: a car rental for instance, a hotel or a flight.

I deal with this kind of input every day, that’s why I called this part “Real world example”.

Now, say that we want all the hotel items (type 2), but we also want to keep the index of the travel in the array, here’s what we can come up with :

The F::merge function is automatically curried, so each item of my array will be merge with the array ['travel_index' => $i] :

F::map and F::flatMap both use our function _map internally. And F::filter also works the same way. This example demonstrate the versatility of what we've built here :

  • We can access the keys of the array we are iterating on (in this example, the index $i)
  • We don’t have to worry about the arity of our mapping function since we’ll give it the parameters it expects.

The latter allows us to combine (compose) our functions without necessarily having to declare a new anonymous function :

Our utility functions (map / filter / propEq ...) can be combined in a elegant, concise and tailored way, hence the title !

--

--