yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Using Curried Functions in Swift

By
-
August 11, 2015

Here at Yeti, we’re big proponents of using Swift for our iOS apps. In fact, every iOS project we’ve taken on since the end of 2014 has been completely written in the language. We chose Swift over Objective-C because of its clean syntax, type safety, and modern features. In this post I’m going to give an overview of curried functions, a feature that Swift and a few other modern languages contain.

So, what exactly is a curried function? Well, let’s break down the words that make up this phrase. We’re all familiar with what a function is (a set of instructions applied to an input which subsequently generates an output). Currying, in the context of computer science, is translating a function that takes in a set of arguments (two or more) into a set of functions that each take one or more arguments. That way, the evaluation of this newly curried function can occur over a period of time, with each call only needing to pass in a subset of the arguments.

An example of translating a regular function into a curried function is as follows:

// adds two numbers and returns the resultfunc add(a: Int, b: Int) -> Int {    return a + b}add(3, 5) // => 8add(9, 10) // => 19// adds two numbers and returns their result (curried)func add(a: Int)(b: Int) -> Int {    return a + b}add(3)(b: 5) // => 8add(9)(b: 10) // => 19var addTo7 = add(7) // => func add(b: Int) -> Int { return 7 + b }addTo7(b: 5) // => 12addTo7(b: -7) // => 0

In the example, there’s two functions that are defined to do the exact same thing: add two numbers and return the result. The first function definition is straightforward and what we’re used to. The second, however, is slightly different in syntax. For each argument in the function add, we separate the argument into it’s own tuple (e.g. (a: Int), (b: Int). This has some serious implications for how we invoke this definition of add (let’s call the second definition of add to be curried add).

In order to return the desired result of Int, the invocation of curried add must wrap each passed argument into its own tuple. Once all arguments have been invoked successively then the desired result is returned.

However, if only the first x < n arguments are invoked (where n is the total amount of arguments) then the return type is going to be a function. More specifically, it’s going to be a reference to a function whose definition is exactly the same as the original but with the first x arguments set to concrete values. As you can see in my definition of addTo7, curried add is invoked with only the first tuple's arguments. addTo7 is then inferred to be of type (Int -> Int) because it equates to the curried add function whose body replaces the variable a with a concrete value of 7. From there, we have access to call addTo7 just like any other function.

The real beauty of this is how we can cleanly define abstractions for a wide variety of problem sets, `add` being only a trivial example to showcase the power of currying. For more documentation on currying, see Apple's official documentation on function declarations

Applying Curried Functions To A Real Application

A few months back, we ran into a problem with one of our client’s iOS applications where we display lists of information on a user’s profile using an abstract class. Abstraction is an amazing tool to use when developing large applications. However, abstraction can prove to be difficult to create when you are creating views and view controllers that are almost entirely the same, save for a few subtle differences.

So here’s the overview of the problem we needed to solve:

Here's a couple of screenshots of what needed to be implemented. The view controller contained multiple sub-views that had no concept of a parent view controller or sibling views, which made it tricky to update the parent collection view controller.

If the cells and content type were the same across all lists, then there’d be no need to worry. As you can see in the above screenshots, these cells differ slightly in content type and cell type. Although the content and styling is different between the different lists, there is still an underlying commonality amongst all of them.

One approach would be to set up a bunch of conditional statements within the main profile ViewController (which is a UICollectionViewController (which gives you a bunch of pre-built functions to render lists of data + event handling of those lists)), fetch all the content from the get go, and display the correct set based off of which button the user clicked.

The problem with that is two-fold. First, the upfront cost of fetching all that data could be tremendous. Secondly, the code will get messy and become a pain to go back and change if other lists are going to be added/removed.

Our Approach

For the profile’s ViewController, we initialize a custom class that contains the navigation bar and its buttons. Inside this instance, the navigation bar buttons are instantiated from a custom class that has an initializer similar to the following:

// On the custom NavigationBarButton classrequired public init(title: String, displayItems: ((NavigationDataSourceAndDelegateProtocol -> ()), dataSourceAndDelegate: NavigationDataSourceAndDelegateProtocol, getItems: (displayData: (resultObjects: [AnyObject]) -> ()) -> ()))// title => Title of the button// displayItems => Function on the profile's ViewController that sets the collection view's  DataSourceAndDelegate to this instance's dataSourceAndDelegate // dataSourceAndDelegate: The instance that handles all data and touch events for the collection view.// getItems: The function that fetches and processes the list content from the API

A vast majority of the work that is implemented for the navigation bar + collection view on the profile’s ViewController is out of the scope of this blog post, but there’s one section in particular I would like to highlight. The getItems argument defined in the above initializer is a function that gets a set of data for a user and sets it on the collection view. The thing is, by the time the NavigationBarButton is instantiated, we lose all notion of what User this button is for. This is where curried functions come in.

Prior to the instantiation of these bar buttons, the functions that we use for `getItems` are curried in such a way that in the first tuple the user is set and in the second tuple the argument is a function that is called on the success handler of the API request.

// a curried function defined in our ProfilePresenter logicfunc getUserVideos(user: UserResponse)(displayData: (resultObjects: [AnyObject]) -> ())) {    interactor.getUserVideos(user, success: displayData)}

Curried Functions saved us from having to pass a User object down from the profile’s ViewController to the custom NavigationBarButtons.


It's simple, but powerful to use when trying to keep code modular and abstract.

You Might also like...

colorful swirlsAn Introduction to Neural Networks

Join James McNamara in this insightful talk as he navigates the intricate world of neural networks, deep learning, and artificial intelligence. From the evolution of architectures like CNNs and RNNs to groundbreaking techniques like word embeddings and transformers, discover the transformative impact of AI in image recognition, natural language processing, and even coding assistance.

A keyboardThe Symbolicon: My Journey to an Ineffective 10-key Keyboard

Join developer Jonny in exploring the Symbolicon, a unique 10-key custom keyboard inspired by the Braille alphabet. Delve into the conceptualization, ideas, and the hands-on process of building this unique keyboard!

Cross-Domain Product Analytics with PostHog

Insightful product analytics can provide a treasure trove of valuable user information. Unfortunately, there are also a large number of roadblocks to obtaining accurate user data. In this article we explore PostHog and how it can help you improve your cross-domain user analytics.

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started