Swift Partially Applied Functions
31 Jan 2018TL;DR
Partially applied functions are really helpful and you’ve probably used them without thinking about it. If you’ve ever used a higher order function like map
or forEach
there is a good chance you have been using partially applied functions. This post is a little exploration into what partially applied functions are, how you may have been using them and some ways of using them going forwards.
What is partial application?
Wikipedia has this to say:
… partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
*arity
is a fancy way of saying “number of arguments a function takes”.
Example of partial application.
A small example might be helpful here. If I need to update multiple cells in a UICollectionView
that are all within section 3
then I would create the IndexPath
s with something like:
I expect most people have written code like the above and not realised that there is a partially applied function hiding in plain site. Let’s make the partially applied function a little more visible by pulling it up a level
To frame this updated snippet in terms of the definition above - we are:
- Taking the function
IndexPath.init(item:section:)
- It has a type of
(Int, Int) -> IndexPath
- Its arity is 2 (it takes 2 arguments)
- It has a type of
- We generate a new function
indexPathInSection3
by fixing the argumentsection
with the value3
- It has a type of
(Int) -> IndexPath
- Its arity is 1 (it takes 1 argument)
- It has a type of
What does this mean?
The above proves that we’ve probably all been doing this without even realising it. The cool thing is now that we have thought about the concept and know what it’s called we can build on this foundation.
Taking it further
It would be nice if there was a simple syntax to create partially applied functions. Ideally it would be a part of the Swift language, as other languages offer this facility and if we have things like @autoclosure
I’m sure this could be built as well. With my imagination running I think the syntax wouldn’t really vary much from what we use now to grab a reference to a function, with the only difference being that you supply some arguments e.g.
This would allow my original example to end up being
Wishing aside we can have some fun building out our own syntax using function overloading and a single cased enum
. The basic idea here will be to create a higher order function that:
1) Takes the function we want to partially apply
2) Takes positional arguments
3) It returns a new function that takes the remaining arguments
To keep this small we’ll look at the code required to partially apply a function that takes two arguments. We’ll start with a function signature (formatted for easier reading)
We start with a function called partiallyApply
which has 3 placeholder types that represent the 2 input arguments and the return. The placeholder types allow us to work with a function that can have any combination of types for the arguments/return. The function
to partially apply is the first argument and its signature it defined in terms of the placeholder types.
We now need to look at the positionally placed arguments but before we can do that we need to have some way to indicate which argument/s we are not fixing. A one cased enum
should serve us well here
This enum
is really just being used as a sentinel value as we can’t use something like Optional
because we won’t be able to tell if the caller is fixing an argument to the value nil
or if they are trying to omit the argument.
Now that we have a way to mark an argument as missing we can fill out the rest of the function signature
The function body we now need to write should return a new function that takes Arg0
as it’s input.
For this to be useful we also need to provide a function overload that makes it possible to mark arg1
as missing.
This gives us the ability to create partially applied functions and choose which argument is fixed - the callsites would look something like:
Finally with these two functions written we can return to our first example and partially apply the creation of an IndexPath
using our new helpers
Putting it all together in one listing we get
Why bother with all this?
To be honest I’m not trying to sell this too hard because I am just showing something I found interesting. This would be much more useful/convenient for me if it was part of the language and the syntax was kept roughly the same as just grabbing a function reference (as I showed above).
I do believe that abstractions can be powerful but if they are too complex to comprehend then the tradeoffs will not be worth it. In this case the benefit of using a helper function would be that it’s super explicit what is going on. In a normal code review I would need to really pay attention with the following code
I would need to make sure that the closure is correctly formed. If the closure is multiple lines long then I would need to mentally parse the whole thing to check for side effects or logic errors. Whereas with the helper function I can relax a little bit and know that there is no funny business going on in the closure and it would be easier to reason about the expected behaviour.
Conclusion
In the above I’ve just given a label to something that we probably all do without thinking about. If Swift had this a language feature it would lower the barrier to entry and hopefully allow people to partially apply more often and in safer ways than hand rolling a closure every time when a simple fixing of arguments is all that is required.