I tend to use the phrases currying and partial application interchangeably. The Intertubes tell me, however, that currying is limited to 1-arity stages. Arity refers to the number of a function’s arguments and should not be confused with Arrietty from the Borrowers.
Swift is tuple friendly and given tuples, I mentallly stretch the “same thingity” thinking to partial application as currying so long as the tuples reflect an underlying coherent structure. This is probably wrong and evil, but what are you going to do?
Swift officially removed currying™ in Swift 3 but it didn’t remove partial application, the ability to create a function of smaller arity, where some parameters have already been assigned.
Consider the following function. It accepts a low and a high bound and clamps a value inclusively within those bounds. You must pass the low and high values each time you call the function:
func clamp<T: Integer>(min minValue: T, max maxValue: T, _ value: T) -> T { return min(max(value, minValue), maxValue) } clamp(min: 2, max: 5, 25) // 5 clamp(min: 2, max: 5, -25) // 2 clamp(min: 2, max: 5, 3) // 3
Partial application enables you to break down the function, to create a reusable component specific to some pair of bounds:
func clampRange<T: Integer>(min minValue: T, max maxValue: T) -> (_ value: T) -> T { return { value in min(max(value, minValue), maxValue) } } // For example let clamp25 = clampRange(min: 2, max: 5) clamp25(25) // 5 clamp25(-25) // 2 clamp25(3) // 3
Swift does not use argument labels in function types, which is why I do not use a label for the value argument. If I were not thinking in terms of partial application, I might have added a value
label for the 3-argument version.
As Mike Ash pointed out today, another way to partially apply a function is to create a separate function or closure. Use positional arguments to populate the missing parameters:
let clamp25 = { clamp(min: 2, max: 5, $0) } clamp25(25) // 5 clamp25(-25) // 2 clamp25(3) //3
It’s a lot cleaner and shorter than the standard approach and, if you like, you can use closure signatures to eliminate anonymous parameters:
let clamp25 = { value in clamp(min: 2, max: 5, value) }
Note that the calls won’t change. Function types cannot have argument labels.
Defaulted parameters kind of look like a partial application, but they really aren’t. You must decide a priori what your defaulted values are, which limits the way you reduce functional arity to a single fixed set of fallback values:
func clamp<T: Integer>(min minValue: T = 2, max maxValue: T = 5, _ value: T) -> T { return min(max(value, minValue), maxValue) } clamp(25) // 5 clamp(-25) // 2 clamp(3) // 3
I find myself using partial application a lot in graphics, particularly when applying perspective or layout transformations. Until now, I’ve been using the standard -> -> ->
approach. Today Mike has made me think that maybe I should consider closures instead, where I’ve partially applied the function to some parameters.
2 Comments
That’s weird, I thought Mike Ash’s approach was the canonical approach. It is, as you say, much cleaner and it doesn’t require the function to be curried/partially applied to have been constructed specially with this in mind. This was also a failing of the official Swift 1 / 2 currying (you had to declare it with multiple parameter lists), which IMO made it almost useless.
Mike’s approach gets uglier the more stages you use… It’s beautiful for single-partial application though, isn’t it?