Currying Closures in Swift

In functional programming circles, fixing the values of one or more arguments to a closure instance and creating a new closure with fewer arguments is often referred to as currying. The resulting closure is generally referred to as a curried closure. (See Wikipedia)

Curried closures are very useful for creating generic closure definitions, and then creating several curried versions of the original with differing parameters bound to them.

Here we describe a left-curry methods with extensible generic arguments. When the curry() method is called, the incoming arguments are bound permanently to the new closure instance so that the parameters 1..N to the curry() call are bound to the 1..N parameters of the closure. The new curried closure is then returned the caller. Callers to the new instance will have their invocation parameters bound to the new closure in the N+1 parameter position of the original closure.

First, a few examples:

Single Argument Closure

Suppose we have a closure f1 with a single argument (Int):

var f1 = { (x: Int) -> Void in println("f1: \(x)") }

We can invoke it as a normal closure:

f1(100)

Resulting in:

f1: 100

With the curry() method, we can freeze the value of the first argument (x) to a particular value and create a new closure:

var f1c = curry(f1, 20)

Now we can invoke f1c like a regular closure, but with zero arguments since the only argument was frozen:

f1c()

Resulting in:

f1: 20

Curry Closure with Two Arguments

Lets add an additional argument to our original closure and call it f2:

var f2 = { (x: Int, y: String) -> Void in println("f2: x: \(x), y: \(y)") }

and invoke it:

f2(100, "b")

We get:

f2: x: 100, y: b

Lets freeze the first argument with the curry method():

var f2c1 = curry(f2, 110)

Now, f2c1 is a closure that accepts one argument. Calling it without parameters like before results in this error:

Playground execution failed: error: <REPL>:358:5: error: missing argument for parameter #1 in call
f2c1()

Additionally, the single parameter to f2c1 is a String. Calling it with an Int gives this error:

//f1c1(1)
Playground execution failed: error: <REPL>:358:1: error: cannot convert the expression's type 'Void' to type 'String'
f2c1(1)
^~~~~~~

When we call it with a String:

f2c1("c")

We get:

f2: x: 110, y: c

As we can see, invoking the curried closure f2c1 resulted in an invocation to the original closure f2, but with the first parameter frozen to the value we supplied when we called curry() (i.e. 110). The second parameter is free to be bound to any String value when f2c1 is invoked.

Left Curry Methods

The code below supports currying for up to two parameters (See gist for full listing). As an aside, Swift allows type aliasing for functions, Swift also supports aliasing with generic types if enclosed in a struct.

struct S0<V> {
  typealias F = () -> V
}
 
struct S1<T1,V>{
  typealias F = (T1) -> V
}
 
//0, 0
func curry<T1, V>(f: S1<T1, V>.F, a1:T1) -> S0<V>.F {
 return { () -> V in f(a1) } 
 }
 
struct S2<T1, T2,V>{
  typealias F = (T1, T2) -> V
}
 
//1, 0
func curry<T1, T2, V>(f: S2<T1, T2, V>.F, a1:T1) -> S1<T2, V>.F {
 return { (a2:T2) -> V in f(a1, a2) } 
 }
 
//1, 1
func curry<T1, T2, V>(f: S2<T1, T2, V>.F, a1:T1, a2:T2) -> S0<V>.F {
 return { () -> V in f(a1, a2) } 
 }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s