Altconf james majors swift func header

What the Func: A Gentle Intro to Functional Programming

Functional Programming is all the rage, but how do you learn about it without a math degree? Easy, watch this talk. We will take a look at some Functional ideas and approaches using Swift. Learn what Functional Programming is good for and how to use some of the more common elements while leaving your sanity

We’re going to talk about functional programming, or (I like to say) everything old is new again (look up Alonzo Church on Wikipedia). Check out all the links and code used in this talk here

Functional Programming (FP)

Functional programming is a tool. If you were to go to a carpenter who was building a house, they would have a multitude of tools that they’re going to use. You would not find a guy who’s all about the hammer. “That’s all I need.” Or the screwdriver, “Best tool ever.” You will not hear carpenters engaging each other in a debate about which tool is best. Why? Because it’s stupid. yes. I can build a house with a hammer, but you wouldn’t want to live in it. This approach of one-size-fits-all, there is some great tool out there, is ridiculous. It is great for certain things, it is terrible for others. It’s another tool in the toolbox.

Let’s look a simple explanation of what makes functional programming functional. First of all, it’s side-effect free, as in there are no side effects. I love it when people give definitions using the word they’re trying to define. Stateless, and it’s trying to use immutable data, which is something that’s beneficial in most paradigms/approaches.

Let’s look at some other paradigm/approaches. Most notably, object-oriented programming. This view is an object; this window is an object; this icon is an object. With object-oriented programming, your basic building block is an object, it’s in the name, and the way you’re dealing with an object is it’s a way of combining variables, which are state, with methods/functions that work on those variables, change that state. If we look at a simple object, it’s has a property, a function that may change that property. You’re dealing with local variables that represent state, you’ve mutable data that is going to change the state and the object, and you are going to have side effects (that’s generally how things in OOP work). When you’re thinking of a button on a screen, it’s easy to rationalize that as an object. This is an object that has some side effect. You tap on it and some other action happens. Mutable data - may change the name of the button, it may change the color, it may change all these other things, and it’s going to change state. It’s going to go from on to off, or any other issues.

We live in a world of those big tubes that carry stuff around; data gets passed back and forth. We’ve had to change some approaches because we have programs that are doing the same thing over and over, working on large datasets - object-oriented programming isn’t always the best approach. Plus you throw in that we now have multi-core processors in everything from your hair dryer to your laptop. We’ve all come across those issues of crossing thread boundaries, getting data, sending data, trying to display data, and bad things tend to happen. Object-oriented programming, not the best approach, but functional programming has some benefits.

As the name implies, the building block is a function. Functions are stateless; you can see that here’s a function where you’re trying to change a value, you get a new version back.


func function(value: Type) -> Type {
    let newValue = /* Transform ‘value’ */
    return newValue
}

You don’t get a changed version - no state to change. Values are passed into the function. State is passed into the function. It’s not residing in an object. The state: that goes out the window, because you don’t have any data changing underneath that is going to change the state, beneficial. And you don’t get side effects.

You can do side effects in functional languages. Swift is not a functional language. It allows you do to do functional things, but it’s certainly not Haskell or Erlang or any of these other punishing, purely functional languages. In those languages, you have to jump through hoops to have side effects. In Swift it’s easy. But the functional approach is to limit that action. Before you had a button, now you have a function. If I say two plus two, you think of a mathematical function, you don’t think of an object that’s going to somehow add numbers together and give you back a value, you think of a function.

Get more development news like this

I can run this function over and over from now until the heat death of the universe, and I’m going to get the same result. There is no side effects, no mutable data, no changing state.

What is a Function

A pure function takes some input, does something to it, and produces an output. Give it the same input, you always get the same output. That’s beneficial. You know what a function is, and Swift has the keyword func. You have a signature, then you have a body.


func squareANumber(x: Int) -> Int {
    return x * x
}

Closures are important in Swift, and in functional programming. This is in effect a closure expression, refer to as a function literal - you are saying I want this code, these things to happen, and I’m going to assign it to this variable name, or this constant name in this case.


let squareANumber = { (x: Int) in x * x }

There’s not much difference between a closure and a function, and we’re going to use both interchangeably (sometimes they’re not fully interchangeable). A closure is a function without a name, aka an anonymous function, and a function, it’s much a closure with a name. Closures, you’ve probably also heard the phrases anonymous function, a block, inline function, lambda expression.

Side-Effect Free

If we revisit our friend the pure function, something goes in, something comes out. Doesn’t reach out to the world around it, doesn’t make network calls, doesn’t access the database. You hand it something, it works on it, it hands you something else. Again, a pure function. In object-oriented programming it’s slightly different. You provide this function with an input, it may reach out to another object that could do something else, which could set off an entire chain of events, hopefully none of which started a nuclear war. And you might get an output.

You can see how this gets more difficult to reason about. I press this button and my toaster turns on. I didn’t program that. Maybe you did, but that’s a problem. Being able to reason about what things are happening becomes easier if you use pure functions. Input goes in, things come out, bed goes up, bed goes down, bed goes up.

func add(_ lhs: Int, _ rhs: Int) -> Int

Let’s look at an example - add. You have a method signature, it gives you an output. I can call this over and over and over, always going to get four. Something to keep in mind, you want that predictability. You want the lack of state. I don’t have to change something else for the answer to be four. I say here’s two and two. I don’t have to make a network request. The user didn’t have to enter any authentication. I don’t have to know if they’re authorized to add this number.

Add up all the variables, all their potential values, state.

State


class ShapeClass {
    var shape: Shape
    var lineColor: Color
    var fillColor: Color
}

A simple example is the shape class; it has three variables. Let’s say that each one of these properties could have three potential values. We now have 27 possible states for this one particular object. Let’s look at the possibility of having thousands, tens of thousands of objects in an application, all with the potential of having a multiple number of internal states: it gets hard to rationalize all the potential cases, all of your edge cases.

What are the normal cases? What are the things that may happen once in a blue moon? What are the problems that can come up with that? Functional programming is trying to eliminate that. You’re not going to keep a bunch of state around. You are going to take different approaches. Because we’re trying to avoid this thing has state, which is going to cause it to do a different thing.

Non-Mutable Data

Basic idea: once you set it, you can’t change it.

In Swift we use var to change things. It’s 10; now it’s 30. Now, we’re going to do constants. You set it not changing anything. If you want to change it, you have to make a new one. In this case, let’s create a numbers struct. It has an array of numbers. You want to create a numbers struct, you hand it an array, it will then store that array. If you want to add a number to that array, you’re getting a brand new struct back. Take the struct, copy it, add something to it, use that copy to initialize a new version. Nothing is changing inside. There is no state. There is no mutable data. You want to make a change, you’re getting a new version. And this is the reason functional programming is not the best approach. What if you want to draw something on screen?

According to a strictly functional approach, you are not changing things in the view, you’re creating a new view. You want to click on a button and have it change color? You’re going to create a new button. It’s probably not the best approach for many user-interface heavy apps.

Value Types not References Type

Swift loves their value types. Let’s say that you had five $1 bills. You are the owner of those bills. If you want to give those bills to someone else, you’re no longer the owner, that person is. That’s a value type. You are not making a reference to it, you are handing it from person to person to person. You give up ownership, someone else claims ownership, you no longer have access to that.

Reference types are a check pointing to the money in your bank account. I can write a check or a number of checks. If I only had $5 in my account and I wrote 10 checks for $5, the first person who cashes the check is the winner. But think about the potential problems that that creates. We deal with having to figure out if we’re going to use like a known self and all these other things to reduce this retention of objects. Don’t use checks, use cash. Worry about here’s a value, here’s the struct, it’s now your problem, I longer deal with it.

Functions as ‘First-Class’ Type

Functional programming, you are not going to find a whole lot of reference types. You’re going to find value types, and in Swift, value types include your friendly enum, structs, value types include enum, structs, e.g. arrays, dictionaries. The majority of types in Swift are value types, but there are reference types, you call them classes and functions, because in Swift a function is a first-class citizen. It is a value type, but it needs to be treated not like a value type. Everyone has their own version of this function. No, everyone points to this same function. It’s a reference type.

Recap of the simple explanation: side-effect free, stateless, immutable data.

Let’s go back to the idea of functions as first-class citizens. You will see a number of these incredibly useful functions popping up. You can use them in any application.

It’s function, it takes a type, returns a type. The type happens to be Int, but it’s a type. We create a constant that is the result of applying this function (that’s what the value of that particular constant is). We determine that that type, or rather the compilers determine that type based on the return type of the function.

We can also do the same thing by creating a closure and applying it to a constant because we’re dealing with types. We applied the type of the return value of the function and created a constant from that. Why don’t we create an actual constant that is a block of code? We say here’s a closure, notice the syntax, simple, curly braces, input arguments, in separates arguments from body, we don’t in this case need to put a return, and then we put what’s happening inside the code, we’re squaring a value. Here, square Int equals this closure. Closures are weird, but not too crazy, because in Swift we deal with types.


func squareANumber(x: Int) -> Int {
    return x * x
}

let aNumber = squareANumber(x: 5)

let squareAnInt = { (x: Int) in x * x }

let squareInt = squareAnInt

We have a function that takes a type and returns a type, and we have another function that takes a type and returns a type. But we know that functions are types. Why don’t we have a function that, and I’m going to add the red braces, because it makes it easier to read, and you can do this in Swift. We’re going to have a function that takes a type, takes two types, and returns a type. One input type happens to be an integer. The other input type happens to be a closure. Nothing crazy, it’s another type, and it outputs another type that happens to be an integer. What does our magical function do? It applies a particular value to a function. It applies a particular value to a function.


func squareANumber(x: Int) -> Int {
    return x * x
}

func doubleANumber(x: Int) -> Int {
    return x + x
}

doSomething(x: 10, function: squareANumber) // 100

doSomething(x: 10, function: doubleANumber) // 20

We’re applying X to that function. But we’re dealing with types. As long as the signature matches, the compiler won’t complain. I could call our doSomething function, pass in a value, and tell it what we want to do. We’re going to square this.

It’s a function, a closure. We get a hundred. Do it again with double the number. Here’s the value 10, which is of type Int. Here’s what I want you to do, which is of type closure, function. What do you get? 20.

Here’s a type. We’re going to apply it. Here’s another function. Notice that the signatures do match. It’s going to this particular function, takes type Int, returns type Int, doSomething requires that the method signature doSomething requires that the method signature that you pass, the function, the closure you pass into it, has the signature, it takes an Int, returns an Int. It’s like magic.

We do the same thing, but in a slightly different way. We don’t need a function, we can create a pure closure, an anonymous closure or we can say what function, instead of giving you a named function, I’m going to give you the code I want you to run. I’m going to give you a closure. Notice that the method signature matches. Our closure takes type Int, and through type inference, is going to return an Int. We are going to dispense with the boiler plate. We can make this easier. We don’t need the return. Let’s get rid of that, because it already knows the signature says it’s going to return something.

It’s going to return type Int. We don’t need the return there. Let’s make this even more elegant, because now you’re saying here’s everything I need you to do, and as long as you pass in something that matches the method signature, you’re good to go. You could have a function that returns its input, doesn’t make a difference. The doSomething function doesn’t care, as long as you give it the correct signature, off you go. Notice that in this example, we’ve changed how the signature looks. We now have doSomething with X parentheses open and close, but then we have our curly braces and our block after it. It’s called trailing closure syntax. If you are working with functions that have closures as the last argument they take, you can do this, you can omit the parentheses and you can hand over the block.

Let’s look at a slightly different example.

Here’s a function that takes type Int and returns a closure, it’s another type. Let’s take our functions of squaring and doubling and put them inside this function, and they are most cleverly called inner functions. I think the name’s obvious. And then we’re going to do bit of work. We’re going to say if the number you pass in is even, square the number. Return, rather, the function, square a number. If the number’s odd, return the function, double the number.

But we can make this bit cleaner, because we can do away with the function boiler plate and go right to giving it a closure. We say this name represents this body of code. I can return these, these closures that we’ve named, that we’ve more or less applied a name to almost functionally.

But we can go one step further. We can return the closure. We don’t need to give it a name and then pass that back, we can give it the body of code that we want to return. You’ve simplified this process and reduced mental tension. I’ve got a function, an inner function, has to do this. If you pass me an even number, here’s the code I want you to use. If you pass me an odd number, here’s the code I want you to use. It gets to the point. If the number you’re given is even, hand this code back. I have to create the function, name it.

Higher-Order Functions

The example would be doWhat, here’s an even number, I’m going to capture the resulting closure with this constant, do the same thing with an odd number and then we apply it.

It is a function that takes a function, a closure, as an argument. Then you saw a function that takes a function, a closure, and returns it. Congratulations, you learned what a higher-order function is. A function takes a function as argument, returns a function as an argument, does both. Effectively it’s saying it’s a function that takes a type. It’s a function that returns a type. It’s a function that take and returns a type. The type happens to be a closure.


typealias IntFunc = (Int) -> Int

func takeFunction(x: Int, function: ((Int) -> Int)) -> Int {
    return function(x)
}

func returnFunction(x: Int) -> IntFunc((Int) -> Int) {
    if x % 2 == 0 {
      return { (x: Int) in x + x }
    } else {
      return { (x: Int) in x * x }
    }
}

This function needs this signature. This function will take as an argument, any function or closure that matches the signature. I gave it a name because it looks cleaner. Using type aliases can help to remove some of the cruft.

Map, filter and reduce are the baseline higher-order functions.

Map

Map deals with two generic types, takes a function and returns a collection of some type. If we were to say here’s an array of numbers we’re going to call map, using trailing syntax, we’re going to hand it this function.

func map<T, U>(function: (T) -> U) -> [U]

let new = [1,2,3,4,5].map { 10 * $0 }

Here’s a function. You take a number, you multiply times 10. Here’s an array, values one through five. Map is going to return a collection of values that are a collection of values that are the result of applying a function using the values of the supplied array. Looks something like this. Let’s take the first value in the array, multiply times 10, capture its result. And let’s do it over and over. Let’s apply this same function to evaluate in the collection, and let’s grab the results.

What you get back is a brand new array. You don’t get back the old array changed. We’re saying take this function and apply it to all this stuff. By the way, this is what’s referred to as a functor. Anything that you can run map on is a functor.

Generic types

T, U, those are generic types. You can replace T and U with anything. It’s a way of saying I’m not going to tell you the specific type that this function is going to deal with, because it could be any number of different type (integers, strings, doubles, etc.).

Notice the dollar sign zero; that is called a positional argument. That is not the position of the element in the array. It is the position of the arguments in the function. Our particular function only takes one argument (whatever number you’re multiplying times 10). Since we’re zero-based, it says take a value from the collection, use that value and insert it into the first argument of the function. If you had multiple arguments, you could provide it with dollar sign zero, dollar sign one.

T is a type. Dollar sign zero is where T goes, because we only have one argument. We’re going to map this function across all of your values. We get the result that you’re expecting.

Map deals with two generic types, T and U. It then has a function, takes a function that requires type T and returns type U, which ultimately, will return a collection of type U. And what the hell does that mean? They’re names. T and U are placeholder names (they could be anything). You could call T input type and then have type U. You could have type T and output type. Or you could say takes type input type, returns type output type. You’re giving this unknown type a name - that it is easier to reason about. You want A type and B type, because you haven’t said what the type is. You’re giving it a name. If T, type T were an Int, this is more or less how the compiler would think about it. Map, you’re going to take a type, which happens to be an Int, and then you’re going to return some other type, we’re going to call it U. What if you’re dealing with Ints?

Type T is an Int, type U is an Int, that makes sense. It could be Int, string, could be string and Int. It comes down to T is some type: Int, Float, String, etc. U is some type: Int, Struct, String, etc. They could be the same type; they could be different types.

Filter

Filter signature takes a function that has to return a Bool, and then returns a collection where the results of the true are returned, or given to you as a new collection. In this instance we’re going to take one through five, we’re going to filter the even numbers.


func filter<T>(function: (T) -> Bool) -> [T]

let new = [1,2,3,4,5].filter { $0 % 2 == 0 } // new = [2,4]

Our function that we’re passing to filter is take a number, divide it, module two. If the result is zero, it’s an even number. We give it an array, then we have our results. We run our, apply it to our first value. One is not even; therefore, it’s false. It goes away. Two is even - that value gets added to our results that are going to be returned, and it goes on and on until we have gone through evalue in the collection, and we had our results, two and four. Again, takes type T, function is, using type T, returns a Bool, and then you get the results. The results from running filter across this array, two and four. It’s simple, straightforward. You didn’t have to do a lot of jumping through hoops. You said give me the even results of this particular array.

Reduce

Reduce is not the actual method signature. I’m going to give this function an initial value, be it zero, one, whatever, doesn’t matter, then I’m going to give you a function that is going to take the same types and return a type, that ultimately I get a single type, not a collection, I get a single value. I’m going to take this array of one through five, reduce it, we’re going to start with the number zero, and then we’re going to add the accumulator, because you have to keep track of how you’re combining things, and the value that I’m dealing with currently from the collection.


func reduce<T>(initialValue: T,
                    function: (T, T) -> T)
                            -> T

T on the left-hand side is the accumulator; right-hand side is the element that you’re feeding in. Because addition is a binary inline function, we can say this is what we want to do, and the compiler will know that. It takes two arguments, not a problem. That fits the signature of what I require.

We have our function, which is to add the accumulator to the value that we’re hitting in the array. We’re adding up all the values in the array. We have our array. Then we’re going to get our result, which is going to be a single integer value. Then we have our accumulator. This is what internally reduce is dealing with. We provide an initial value (we can start with zero or with anything).

It initially sets the initial value we give it to the value of the accumulator, then, it’s going to run this across all of our values. Now the accumulator is zero. We add that to the first value, we get one. Now the accumulator is one. We add it to the next value, three, six, 10, ultimately the value we get, 15.

It’s that method signature’s wonky - reduce. Start with an initial value, the function that you’re providing will take two values, one is the accumulator, one is the value of whatever element in the array or collection you’re dealing with. The function has to return a single value. It can’t return two Bools, and all this weird stuff or a function, and ultimately you’re going to get a single value back.

In our example, the simpler one was to say dollar sign zero, which is the accumulator, dollar sign one, which is the value in the collection. But since it isn’t, takes two arguments, no problem. This is the definition of declarative programming. Imperative is how something needs to happen, declarative is what you want to have happen.

Imperative Programming describes the algorithm

Imperative programming is the algorithm: take a value, add another a value to it, return that value, then you can create a for loop. That’s the imperative approach: I need this to happen and this to happen and this to happen.


func arrayTimesTen(array: [Int]) -> [Int] {
    var outputArray = [Int]()

    for number in array {
    var newNumber = number * 10
    outputArray.append(newNumber)
    }

    return outputArray
}

Here is our reduce in effect. We create an output array of the same type as the input array, then we’re going to do a for-in loop. Give me a number, multiply times 10, add the result to the output array, and let’s go through everything, then we return the array. All we wanted was to multiply a number times 10. Why do we need all the boiler plate? Why do we need to create a for loop? Why do we need to create temporary storage?

To take that idea and apply it to the three higher-order functions, with map in this instance, you’re saying, do this. I want the result of multiplying 10 I want the result of multiplying 10 times number in this array. Filter, I want the even numbers back. And reduce, add up all the elements in the collection. That’s it.

You are taking the approach of here is what I want you to accomplish, not here is how I want you to accomplish it, which makes things easier to read, and it is quicker. You get to the intention of what the person was trying to do not that they understand basic syntax in Swift. “They want everything in this array multiplied times 10”, “they want the even numbers” - it reduces some of the mental overhead of trying to figure out what the hell people want. It is easier than saying: “here’s this function that iterates through everything, has an accumulator, etc”.

You can also do the same thing with an actual function. As long as the function matches, you don’t have to hand it a closure, you can hand it a function. We can create these simple functions. Do the same thing with map. Do the same thing with filter. Here’s a function that says if it’s even or not. You can do the same thing with reduce. I made this function. I hopefully have named it well enough that people can figure out what it is, and use it.

Currying

Currying is described as the partial application of a function. I consider it the initialization of a function. We have to initialize objects. We have to initialize structs. This is functional programming. Wouldn’t it makes sense you have to set up a function?


func addClosure(x: Int) -> ((Int) -> Int) {
    return { y in x * y }
}

let add = addClosure(x: 10)
add(10)     // 100
add(100)     // 1000


func timesClosure(x: Int) -> ((Int) -> Int) {
    let z = x
    return { y in z * y }
}

let times = timesClosure(x: 10)
times(10)   // 100
times(50)   // 500

We’re going to create a closure, and return a closure, but notice that we are going to initialize this closure with a value. We pass an X to this function, it will then take X and stick it in this closure. A closure encloses around values that are outside of its scope. X makes a reference to something outside of its own scope. It’s going to grab the value of X, it’s going to return that as a closure. The return parentheses its closure, Y and X times Y, we’re giving it input arguments, and here’s the function body. We’re saying add is going to have X preset as 10.

If I call add, why’d I call it add when it multiplies? We pass it another value. Instead of saying I want 10 times 10, I’m saying multiply whatever you have preset times 10, times a thousand by a hundred.

Here’s an example of let’s grab X and let’s create a constant that’s outside, and then we’ll use that constant in the closure that we return. It’s enclosing around the value that’s outside its scope. Same idea: we preset one of the values in the function, and then we pass in the values that we want it to calculate. The rest of the code is available in the code playground here.

Next Up: Using Realm Seamlessly in an RxSwift App

General link arrow white

About the content

This talk was delivered live in June 2017 at AltConf. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

James Majors

After spending 25 years in the audio and video world I decided that I needed a change so I went back to one of my first passions, writing software. I was fortunate enough join the team at POSSIBLE Mobile where I get the chance to write apps people use and occasionally talk at conferences.

4 design patterns for a RESTless mobile integration »

close