Functional Programming in PHP (Part 2) — Currying

Viktor Daróczi
Nerd For Tech
Published in
11 min readApr 2, 2021

In the previous article we walked through the foundations of Functional Programming (FP) in PHP. This was by far not a comprehensive introduction, but rather mentioned the most important concepts of FP along with the PHP features, that are necessary to start applying FP in PHP as soon as possible. In this part I assume you’re already familiar with those basics mentioned in the introduction, and I will drop more concepts in as we proceed with currying.

Spicing your code up

Although it sounds fancy, currying has actually nothing to do with your favorite turmeric mix, but derives its name from the American mathematician Haskell Curry (you will definitely remember this name). Nevertheless, we will see that currying provides nothing less than spicing our code up.

In pure functional languages, like Haskell (surprise!), you can feed your functions with less arguments than what they expect (←not accurate, they always expect maximum one argument, but for the sake of simplicity, we will linger in this perspective), and they resiliently reply with a new function which expects the remaining arguments, while capturing the ones you’ve already provided.

This is currying. (In fact it’s auto-currying, because you didn’t need to explicitly call curry on the function, as we will.)

We can illustrate this with some symbolism as follows.

If we think of a function as a mapper that maps inputs to outputs, we can symbolize them like this:

<input_type> → <output_type>

And functions taking multiple arguments like this:

<input_type_1>, <input_type_2>, <input_type_3> → <output_type>

Then we can represent curried functions like this (example for 3 arguments):

<input_type> → (<input_type_2> → (<input_type_3> → <output_type>))

Meaning that instead of waiting for all three arguments, our function just returns another function what takes the next argument, and returns another function which eventually takes the last argument, and returns the output.

How many input arguments are there, is not important.

<input_type_1>, ...<input_type_n> → <output_type>

would be curried to:

<input_type_1> → (... → (<input_type_n> → <output_type>))

We can also skip the parentheses, because our function always returns one single output, so it’s obvious that everything on the right side of the arrow is just the output:

<input_type_1> → ... → <input_type_n> → <output_type>

The important point here is that we can postpone gathering all the required data, and store the already available data within a new function.

In practice that means, if you expect 5 arguments, and you fed none, you get the a function keep on expecting 5 arguments or less. If you fed this function with just 1 argument, it returns a new function expecting the remaining 4 argument, while capturing the one that was already provided. And if you feed this function with 2 arguments, the returning function will expect the remaining 3 arguments, and so on. The good thing is, that the returned function will also do us this favor, and we can feed the function with arguments one by one. Or two by one. Or grouped by any way you like.

What if you provided all the required arguments? Then your function finally gets executed as we’ve seen above, and returns its result.

This is a very powerful concept, and lets us get rid of a considerable amount of local variables.

At this point you can try to implement your own version of the curry function, which takes a function, and keeps on returning embedded functions that take the next argument until all parameters of the original function are satisfied, in which case it simply returns the result of the original function called with the arguments provided.

In the meantime, I will talk about variable-length argument lists. See you in a minute.

“a boa constrictor digesting an elephant”

Just like the snake on the famous drawing, our functions are also capable to swallow and digest almost anything if we let them do so. That means that they can accept a variable amount of arguments, and we don’t need to specify how many of them. It can be zero, one or more.

This is done by marking our functions with the ‘... token’ — as PHP calls them. In other languages it is called the ‘ellipsis notation’ (C/C++, Java), or ‘spread operator’ (JavaScript/TypeScript), but PHP curiously avoids calling it any of those names, and uses the ‘... token’ or ‘... operator’ phrases (release announcement) all over the relevant parts of the docs. Even if we don’t call it by name (for some superstitious reasons), they are very useful, and we will use them quite often.

The syntax looks like the following:

function variadic(...$arguments) {}

where the $arguments parameter will be an array . This can help us in PHP to feed our functions with arbitrary number of arguments at once, and simplify some code.

Back to our soup…

Cooking with curry

Our first attempt to use our curry function (which you either already have implemented, or will use my implementation that I provide later) may look something like the following:

If we run this code, it should print:

myc(a, b, c)     =>     a b c
myc(a)(b)(c) => a b c
myc(a, b)(c) => a b c
myc(a)(b, c) => a b c
myca(b, c) => a b c
myca(b)(c) => a b c
mycab(c) => a b c
mycabc => a b c

If we took the Test Driven Development approach, this could be our (sloppy) test. We created a curried version of our simple function $myfunc (line 5), three other functions feeding arguments to them one by one (line 7–9), then started to call those functions like crazy.

Our hypothetical curry function apparently accepts only one argument, the function to be curried.

$myfunc will be our original function which has a fixed number of parameters, and just creates another string from them. There’s no magic happening here.

The part of creating the functions represents the idea that a curried function keeps on currying until all the required amount of arguments are passed to the original function. Note that we don’t need to repeat the arguments that were already given, because they are captured in the returned function, and are waiting for the rest only. We can assign those functions to a new variable like we just did, and they inherit the parameters that were already provided.

The lines printing to the screen show the various ways such curried functions are possible to be called. As you can see, no matter how we call them, or how we group the arguments, they always return the same string as soon as all the arguments were provided: a b c .

Our test don’t do a particularly good job, because some important cases are left out. For example, what if our original function does not expect any argument? Then — as specified — the result of the calling of the function is returned. And what if we provide all the required arguments immediately? For that we can try the following:

curry($myfunc)('a', 'b', 'c')

By definition, it will return the same output: a b c . So why not use our friend with the three dots, and accept also a variable-length argument list? Well, that depends. Let’s assume we have it, and we can make calls like these:

curry($myfunc, 'a', 'b', 'c'); // 1.
curry($myfunc, 'a')('b', 'c'); // 2.

Is that any better? I see no point in the first one, because we could call our original function directly. If curry returns the result of the original function that is. But if it returns a function (callable) all the time, then we need another call to trigger the original function like this:

curry($myfunc, 'a', 'b', 'c')();

That also doesn’t make much sense for me. The only reason I can imagine to do so, is to make sure our curry function always returns a callable.

The second example of putting some arguments right in curry while leaving some out for later, does not improve the syntax at all. In fact, it just raises some confusion sometimes seeing completely different kind of arguments in curry following the first one, our original function. I prefer to keep the API clean, and work with the minimum necessary amount of parameters possible. For this a signature like the following looks optimal to me:

curry(callable $fn)

No noise, no clutter, just bare necessities. OK, move on.

Serving dishes

One does not simply take pictures on food if also wants to stimulate appetite. It turns out that serving plays a crucial role in that. How the food is placed on a dish, how it’s lit, what kind of camera is used, etc.

In the above example usage, we can find 23 instances of $ character. In 18 lines of code. That’s more than 3% of the total characters. A considerable amount of clutter. As mentioned earlier, we don’t even need them. We’d be happier to have constants instead, but our options to do so are also not very bright.

For a while, let’s play around with the idea of immutable values pretending there’s nothing wrong with our implementation from the previous part. So define an immutable value like this:

function immutable(): string { return 'totally immutable'; }

Formerly we defined variables to store functions. So let’s define those as well to be immutable:

function immutableFn(): callable { return fn() => 'yeah'; }

Let’s see how we could use currying with these.

Outputs:

Immutable greeting: Good day, Tülin!
Good day, Tülin!
Hello, Tülin!

So we’ve seriously reduced the use of variables. Everything that should not change became immutable (implemented by functions), and we also improved code reuse by higher-order functions and currying. I would risk to say we also improved readability.

We have an immutable value for our main function called greet . It just returns a function that takes two arguments, a form and a name.

We have two immutable values for the two supported forms, formal and informal with values ‘Good day’ and ‘Hello’ respectively.

Then we defined two new immutable values, using some FP magic, feeding our original greet function with predefined values coming from formal and informal and giving them the name of greetFormally and greetInformally .

The important takeaway here that we can pass data to our functions whenever they become available, and that we can inject functions to other functions in a way similar to dependency injection or the Strategy design pattern. This helps us separate the concerns, not repeating ourselves (DRY), and keeping single responsibility. Yeah, all that OOP stuff, just easier.

Note that I only mentioned these to help you understand some concepts provided you’re already familiar with these ideas, but they are not one-to-one analogies, and you don’t need to worry if you don’t completely understand them. To proceed it’s enough to understand how our code samples work.

Finally, we define another immutable with the value ‘Tülin’ called tulin, and another one called girl with the value already stored in tulin .

All our components are reusable. We built new functions from existing ones like playing Lego. We added configuration relatively early in the code creating new, specialized functions, and fed them with the most tangible values at the latest possible point. This organization principle will be further emphasized as we collect more and more FP tools for our toolbox.

Remember that the named components we used to store immutable values are not supposed to be functions in the PHP sense. They happened to be functions only because of our implementation of immutables uses functions. That explains the upcoming abundance of parentheses:

greet()(formal(), tulin())

and

greetFormally()(tulin())

These may look weird at first sight, but in an ideal world, they would look like this:

greet(formal, tulin)

and

greetFormally(tulin)

That ideal world is similar to how Scala syntax works. So how would the whole snippet look like in an imaginary PHP language?

Assuming there’s a construct for defining immutable values, and it’s called let, then it could look like the following:

let callable greet = fn(callable $form, string $name): string => $form() . ', ' . $name . '!';let callable formal = fn(): string => "Good day";
let callable informal = fn(): string => "Hello";
let callable greetFormally = curry(greet)(formal);
let callable greetInformally = curry(greet)(informal);
let string tulin = "Tülin";let string girl = tulin;// reassignment does not work:
// let string girl = "Somebody else";
greet(formal, tulin);
greetFormally(tulin);
greetInformally(girl);

Way better! Now stop dreaming and go back to work. We have a function to implement.

Here’s a beautiful Bavarian scenery to help you prepare for the mental load — courtesy of the author

Implementing curry in PHP

There are several ways to implement currying in a language, and also several ways to implement it in PHP. For now you might have your own version, which you checked against the usage examples. If it worked well, congratulations, you did it right!

For reference, I provide my own implementation, which has a focus on defining a clear interface, and hides implementation details from the users as much as possible.

All the examples were tested using this implementation, and it also serves as a basis for all the rest of the functions that we will implement during this series of articles.

First of all, don’t get confused with the class keyword here. This is not an OOP class in the strict sense (and we also don’t allow instantiating it— by line 19), it just helps us with information hiding. I don’t want to expose the internals of my function, it’s not for public use. Encapsulation is on the rescue.

We use the cool PHP feature of reflection (line 4), which provides some meta information about the things being used, for example how many parameters our function has. It also makes possible to call our function with some arguments (line 16).

Our essential curry function is implemented here as a private method. The public method only provides a clear interface and does some preparation. _curry could be a recursive function, meaning that it would call itself, but we don’t bother with that. We simply let the user call it if they need it (line 14). In our case until all the arguments are provided. If nothing is missing, then simply calls the original function with the collected arguments, and returns its result (line 16).

The rest is just PHP mambo jambo: how we pass data by parameter list, and how by capturing the context with an arrow function (line 14). Also in this line, we state that we don’t know how many arguments will be fed next time to our function, but whatever is provided, just forward it to our private curry weightlifter with the array unpacking ... operator that we don’t call by name.

Our function stops executing there, and only keeps on collecting the arguments for the original function, when they are provided. And because it does so using our all-aware private function, it can recheck whether all arguments became available, and then eventually invoke the function.

This is however not how functions are implemented in FP, but it’s how we can implement an essential FP function in PHP. Whichever tricky way we implement these functions, they remain rather short, testable, and their internals never seen again. Hopefully, we won’t need to touch this part ever, yet we can use it happily in our daily business of FP, knowing that it does only one thing, and does that right.

You can improve this function, or tailor it to your needs. As always, the most important thing is not how something is implemented, but what interface it defines, and what guarantees it gives, or what laws it obeys.

That’s all it takes. Make sure you have a working version of curry at your disposal, tested against the use cases provided, because we will use it heavily from now on.

In the next article, I’ll talk about function composition, how we can combine several functions into one in a clean way, and also about modules, which we can use to organize our code better while still having the benefits of encapsulation, so stay tuned!

--

--

Viktor Daróczi
Nerd For Tech

A software engineer focusing on the fun part of computing.