Blog options

Function Value Types & Closure Syntax

Function Value Types & Closure Syntax

 Function Value Types

One of my greatest "ah-ha" moments when learning functions, was when I understood that functions are actually values. Just as a String, Int, Bool or Double can be assigned to a variable, so can a something like this:

Int -> String

This is simply a type that "takes an Int and returns a String", which we can set as avariables type like you'd expect.

var strValue : String -> Int

Let's create a simple function that conforms to our type.

func stringToInt(i: String) -> Int {
    return Int(i) ?? 0

Here, our function simply takes a string and attempts to convert it to an int. If the conversion fails our stringToInt function returns zero.

Now that we have a matching function for our String -> Int variable, we can simply assign our function to our variable.

strValue = stringToInt

Of course we can use our new variable directly if we like by doing something like this:

let result = strValue("44") * strValue("56") // prints 2464

Function value syntax can become a bit unwieldy to work with. It's much easier to read "String" that to read a type like "(String -> Void) -> (String)". Fortutnately the Swift team gave us the option to associate a type alias for any type. Let's create an associated type for our function value type.

associatedtype Destringer = String -> Int

While we always have the ability to use our function value types directly, with our new associated type, we can now write functions with cleaner signatures.

func multiply(values : [String],fn: Destringer) -> Int {
    var result = 0
    values.forEach { str in
        result += fn(str)
    return result

Then we can put our functions to work.

let vals = ["1","2","3","4","5","6","7","8"]
var combineStringValues = multiply(vals, fn:  strValue)

print(combineStringValues) // 36

 Closure Syntax

 The last line of code I shared with you can also be written using both closure and trailing closure syntax. But before I show an example, I wanted to breifly point out that a closure is a function. I've added a link to Apple's developer guide on the topic. So, here's the same function as befure using closure syntax:

let closure = multiply(vals, fn: { item in
    return strValue(item)

However, the more common way to use closures, is by using the tailing-closure syntax. We can only use trailing closure syntax if our function is the last item in our method signature. We simply eliminate the parameter name and move the last parenthesis after our last "regular" parameter.

let trailing = multiply(vals) { item in
    return strValue(item)

At first this may not seem as intuitive as our first version where we passed our function like this: multiply(vals: fn: strValue). However, I prefer the trailing closure syntax because I think it makes the separation between our input and our function more comprehensible. Here's how I read the function above:

The multiply function takes a set of values...

let trailing = multiply(vals)

And it runs my given function once for each 'item' in my set of values, returning the result.

let trailing = multiply(vals) { item in
    return strValue(item)