Swift Partially Applied Functions

TL;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 IndexPaths with something like:

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map { IndexPath(item: $0, section: 3) }
// => [[3, 3], [3, 6], [3, 7], [3, 9]]

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

let indexPathInSection3: (Int) -> IndexPath = { item in 
  IndexPath(item: item, section: 3) 
}

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map(indexPathInSection3)
// => [[3, 3], [3, 6], [3, 7], [3, 9]]

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)
  • We generate a new function indexPathInSection3 by fixing the argument section with the value 3

    • It has a type of (Int) -> IndexPath
    • Its arity is 1 (it takes 1 argument)

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.

let indexPathInSection3 = IndexPath(item:, section: 3) // => (Int) -> IndexPath

This would allow my original example to end up being

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map(IndexPath(item:, section: 3))
// => [[3, 3], [3, 6], [3, 7], [3, 9]]

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)

func partiallyApply<Arg0, Arg1, Return>(
  _ function: (Arg0, Arg1) -> Return,
  ...
  ...
) -> ...

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

enum DeferredArgument {
    case `defer`
}

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

func partiallyApply<Arg0, Arg1, Return>(
  _ function: (Arg0, Arg1) -> Return,
  _ arg0: DeferredArgument,
  _ arg1: Arg1
) -> (Arg0) -> Return

The function body we now need to write should return a new function that takes Arg0 as it's input.

func partiallyApply<Arg0, Arg1, Return>(
  _ function: (Arg0, Arg1) -> Return, 
  _ arg0: DeferredArgument, 
  _ arg1: Arg1) -> (Arg0) -> Return {
    return { function($0, arg1) }
}

For this to be useful we also need to provide a function overload that makes it possible to mark arg1 as missing.

func partiallyApply<Arg0, Arg1, Return>(
  _ function: (Arg0, Arg1) -> Return, 
  _ arg0: Arg0, 
  _ arg1: DeferredArgument) -> (Arg1) -> Return {
    return { function(arg0, $0) }
}

This gives us the ability to create partially applied functions and choose which argument is fixed - the callsites would look something like:

let square = partiallyApply(pow, .defer, 2)
square(2) // => 4
let raise2ToThePowerOf = partiallyApply(pow, Double(2), .defer)
raise2ToThePowerOf(16) // => 65536

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

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map(
    partiallyApply(IndexPath.init(item:section:), 3, .defer)
)
// => [[3, 3], [3, 6], [3, 7], [3, 9]]

Putting it all together in one listing we get

//: Playground - noun: a place where people can play

import UIKit

enum DeferredArgument {
    case `defer`
}

func partiallyApply<Arg0, Arg1, Return>(_ function: @escaping (Arg0, Arg1) -> Return, _ arg0: DeferredArgument, _ arg1: Arg1) -> (Arg0) -> Return {
    return { function($0, arg1) }
}

func partiallyApply<Arg0, Arg1, Return>(_ function: @escaping (Arg0, Arg1) -> Return, _ arg0: Arg0, _ arg1: DeferredArgument) -> (Arg1) -> Return {
    return { function(arg0, $0) }
}

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map(
    partiallyApply(IndexPath.init(item:section:), 3, .defer)
)

print(indexPaths)

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

let idsToUpdate = [ 3, 6, 7, 9 ]
let indexPaths = idsToUpdate.map { IndexPath(item: $0, section: 3) }
// => [[3, 3], [3, 6], [3, 7], [3, 9]]

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.

Swift static library in Objective-C

Xcode 9 added the ability to create static libraries with Swift code. Everything works great if you have an all Swift project, you link your library, import the module and away you go. To get your shiny new static library to be visible in Objective-C you need to do a little more work.

These instructions are based on a setup where you will build the static library as a subproject of your main app, you'll need to make adjustments as required if this does not mirror your setup.

Step 1 - Create/configure the subproject

  • Create the subproject using File -> New -> Project -> Cocoa Touch Static Library.

Cocoa Touch Static Library

  • Give the project a suitable name and click Next

Give the project a name

  • Add the static library project to your existing project

Configuring the save file dialog


Step 2 - Link the static library with the main project

  • Navigate to the target's General tab for your main app

Navigate to your target's `General` tab

  • Add the static library to Linked Frameworks and Libraries using the + button

Link library

For a pure Swift project you should be done now. For a mixed project carry on.


Step 3 - Add the generated swift header to the search path

  • Navigate to the Build Phases of the static library

Static Library `Build Phases`

  • Add a new Run Script Phase with the following code and give it a sensible name
target_dir=${BUILT_PRODUCTS_DIR}/include/${PRODUCT_MODULE_NAME}/

# Ensure the target include path exists
mkdir -p ${target_dir}

# Copy any file that looks like a Swift generated header to the include path
cp ${DERIVED_SOURCES_DIR}/*-Swift.h ${target_dir}

Static Library `Build Phases`

At this point you should be able to import the static library using #import <FeatureA/FeatureA-Swift.h> and start using your Swift code that is annotated with @objc.


Step 4 - Create a ModuleMap

Depending on how your project is set up you can run into issues where the generated swift file for your main project imports the new static library. The issue here is that it will import using module syntax (@import FeatureA;), which your project will not currently understand.

  • In your static library project create a file called module.modulemap and populate it with something like the following
module FeatureA {
    header "FeatureA-Swift.h"
}
  • Navigate to the Build Phases of the static library project

Static Library `Build Phases`

  • Add the module.modulemap file to the Copy Files phase of your static library

Update `Copy Files`


Wrap up

Following the steps above should get you working with a Swift static library within a mixed Swift/Objective-C project much quicker than I did.


Extras

Whenever I read a post like this I'm obviously grateful for the tips, especially if they help resolve the thing that was blocking me, but I'm always left wondering how someone worked it out in the first place. Here's some rough retrospective notes on the things I did to figure out the above.

Check search paths

I examined all the search paths - you can view these in Xcode by searching in Build Settings. The one that I was mainly investigating was Header Search Path, this setting is blank by default.

I noticed that the Static Library project sets its Copy Files subpath as include/$(PRODUCT_NAME) so I made the assumption that this directory will be searched.

Examine derived data

I started to look in the Build Products Dir to see if I could find my generated Swift.h file but couldn't see anything. You can get to the Products folder by expanding the Products folder in Xcode and then right clicking an item (like the static library) and selecting Show in Finder.

At this point I was started to think that maybe the generated header just wasn't being created and this wasn't meant to be.

Examined the build logs

I was just looking to see if there were any errors I had missed or anything else to help me out. This is when I noticed a phase called Copy FeatureA-Swift.h ... - BINGO. The header is indeed being created it's just being put in a place that is not on the search path.

This is when I added the Run Phase Script from above. It basically copies any files following the name *-Swift.h from the DerivedSources folder to the include folder.

ModuleMap

In my particular project I ran into the problem where the main project's generated Swift file imported the library. This meant that the library was being imported using module syntax. This step was not too difficult, I just read a few blog posts on creating a module.modulemap file and got it done.

Extra wrap up

I've tried to give some insight into the things I did to resolve the issue but reading the above can still be misleading. Taken at face value it looks like I just followed a few systematic steps to get to a solution but the reality was that this debugging took many hours, lot's off googling, lot's of swearing and muttering to myself, a few good nights of sleep and a lot of perseverance. Hopefully someone might find this useful (probably just me in a few weeks time when I have forgotten the above).

Quick Tip: Test functions with DI

Testing your code's collaborators is really important but how do you test functions like UIImage(named:) or NSLocalizedString(_:tableName:bundle:value:comment:)?


What are we testing?

We are not interested in testing whether methods like UIImage(named:) actually work as that's Apple's job but we should verify that we invoke the functions with the correct arguments.

Take the following example

class Images {
    
    func jumpSprite(atIndex index: Int) -> UIImage? {
        return UIImage(named: "jump_\(String(format: "%03d", index))")
    }

}

The above is a simple call to UIImage(named:) with some simple logic to build the image name based on the passed index. The thing that we need to test here is the image name construction logic - we would like our tests to verify that if we call the function with the input of 1 that it will invoke UIImage(named: "jump_001").


Dependency Injection to the rescue

To create a seam that allows testing the collaborator we can make UIImage(named:) injectable. Our production code can continue to use UIImage(named:) but our tests can use a different function that allows us to capture and verify the input.

Start by making it injectable with a sensible default

class Images {
    
    var loadImage = UIImage.init(named:)
    
    func jumpSprite(atIndex index: Int) -> UIImage? {
        return loadImage(named: "jump_\(String(format: "%03d", index))")
    }
    
}

In the above we made a couple of changes

  • Added a new variable of type (named: String) -> UIImage? that holds our image loading function
  • Invoke loadImage(named:) instead of directly invoking UIImage(named:)

End by adding some tests

Now that we have done the scaffolding we can add tests that verify that the correct arguments are provided when loading images

class ImagesTests: XCTestCase {
    
    func testJumpSpriteIsInvokedWithTheCorrectArguments() {
        let images = Images()
        
        var captured: String?
        
        images.loadImage = { name in
            captured = name
            return nil
        }
        
        images.jumpSprite(atIndex: 321)
        
        XCTAssertEqual("jump_321", captured)
    }
    
}

Conclusion

The fact that functions are a first-class type in Swift means that it is super simple to use dependency injection to enable testing of functions. It's always important to test our code's collaborators to ensure that we are calling their contracts correctly and with the arguments that we expect to send.