california app design company

Using Curried Functions in Swift

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 result
func add(a: Int, b: Int) -> Int {
return a + b
}
add(3, 5) // => 8
add(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) // => 8
add(9)(b: 10) // => 19

var addTo7 = add(7) // => func add(b: Int) -> Int { return 7 + b }
addTo7(b: 5) // => 12
addTo7(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:

  • There's a user profile view with several sub-views

  • There's a navigation bar view contained within the user profile view with several buttons that (will) link to several lists of content (such as the user's videos, clips, likes, requests, etc.)

  • When a user clicks on one of the navigation bar buttons, the appropriate data needs to be fetched and displayed in the profile’s CollectionView.

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.

  • Data must be fetched from the server using some API endpoint.

  • The server data is returned as a list of something (e.g. videos, features, clips, etc).

  • The server data has to be parsed and displayed on the same collection view using some CollectionViewCell sub-class.

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 class
required 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 logic
func 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.

is a Yeti Alum. I consider myself part engineer, designer, and artist. My professional expertise lies in designing, architecting, and building applications and infrastructure for the web and mobile. My mission is to develop intelligently designed software for industries that have typically been overlooked by the tech industry. Urban planning, personal health, food, and fashion are a few areas that I believe can be radically transformed through great software.

blog comments powered by Disqus
Using Curried Functions in Swift https://s3-us-west-1.amazonaws.com/yeti-site-media/blog/.thumbnails/thingy.jpg/thingy-360x0.jpg
Yeti (415) 766-4198 https://s3-us-west-1.amazonaws.com/yeti-site-static/img/yeti-head-blue.png