A few weeks ago, I got to attend the 1st day of this year’s DO iOS conference in Amsterdam, hosted by CocoaHeadsNL.*

Also known as the University day, this was an experimental workshop on functional programming by Daniel Steinberg — author, trainer, and developer at Dim Sum Thinking — who, spoiler alert, is a great speaker. Daniel has a strong background in iOS and is well familiar with functional programming. (Cool fact: he used to be a Math professor before becoming a developer.)

Roughly said, the conference included some stuff that was easy, some that was intermediate — and some that was a certain kind of magic. In this blog, I’ll share the highlights, as well as some of the best tips, tricks and hints of the day.

*To read up on part 2 of our series on DO iOS, click here


1. Functional Programming

First up: some of the cool features of functional programming, e.g. chaining functions to the result of the previous function. Believe it or not, the image below is made of a struct containing a closure drawing an ‘F’, executed multiple times. This works because each function returns an instance of the struct itself. Nice!

The variable is called ‘f’, and it’s an instance of a struct called Picture containing a closure as a property (the closure draws an ‘F’). In fact, the first picture is created simply by calling f, like this:

f

In contrast, the second one is made by calling:

f.quad(blank, f.rotate(), f)
  .quadFlip()
  .quadRotate()
  .quadRotate()

The key here is that each of the functions—rotate() , quadFlip()and quadRotate() — return an instance of Picture, allowing you to run more transformations in a functional way.

Next, we discussed a couple of the more basic functional programming functions in Swift, e.g. .sorted() , .split() or .map(). Some of the developers creating Swift have a background in this type of programming, so this was a welcome addition to the day.

Nearly everyone in the group had heard of these functions before or had used them at some point — but even so, Daniel was able to show us original ways of how to use them. For example: in order to figure out if the sentence “Was it a cat I saw? ” is a palindrome (reads the same backwards as forwards), you might write a loop to compare the first letter of the first sentence with the last letter of the second sentence (after lowercasing and removing the punctuation). If you go functional, you could do something like this:

zip(string, string.reversed())
  .map(==)
  .reduce(true) { (accumulator, element) in
    accumulator && element
  }

We also implemented a map function manually to see what was happening under the hood, which was simple and cool. After we made an abstraction for it, Daniel followed up with this advice:

“When writing abstractions, go from concrete to abstract.”


Coffee — or rather, tea break! (I’m that one tea guy every office has).


2. Custom Set Implementation

Let’s raise the level! After the break, we practiced implementing a custom set. However, instead of storing values this set only had a variable containing a closure. It looked something like this:

struct MySet<T> {
  let contains: (T) -> Bool
}

Main takeaway: choose a good name for your variables. Daniel even joked about how bad MySet was, saying: “You should be taking naming discussions with your team”. It’s difficult to determine exactly what makes a good name (that’s why discussing it is crucial) — but as a rule of thumb, good code can be read like prose: variables should be names, and functions should be verbs.

Back to MySet: it might look a bit unfamiliar, but it allows you to create a set containing everything conforming to contains.For example, if you want to display all the even numbers in a set:

let evenNumbers = MySet<Int>(contains: {x in x % 2 == 0})

We also implemented all of the functions of a normal set as extensions of mySet struct. For me, this was a great way to gain insights: not just about functional programming and sets, we also dealt with many generics. Some examples of set:

extension MySet {
  static func ||(first: MySet, second: MySet) -> MySet {
    return MySet{ x in first.contains(x) || second.contains(x) }
  }
  static func &&(first: MySet, second: MySet) -> MySet {
    return MySet{ x in first.contains(x) && second.contains(x) }
  }
  static prefix func !(set: MySet) -> MySet {
    return MySet{ x in !set.contains(x) }
  }
}

Lunch!

I met a couple of developers from other companies, and we had a chat about the frameworks that we use, hybrid technologies, and how impressed we were by Daniel’s workshop.


3. States & flatMaps

Back to work. Difficulty level: black magic.

Have you ever wondered how to get a random number generator under test? It might sound easy at first, but the idea of a random number generator is that every time you ask for a number, you should get a completely random one. So how do you actually make it testable?

The answer: wrap your generator in a state. For each execution, the state will provide you with the random number — as well as the next instance of the generator.

The code looks like this:

struct State<S, A> {
  let run: (S) -> (A, S)
}

Looks kind of similar to the set? The concept behind it is identical: a struct containing a closure as a variable. What makes this one special is that the closure takes a state as a parameter — returning a ‘next’ state and a result.

We explored a lot of complicated stuff with states, but probably the most interesting one was writing flatMaps for it. One of the great things about flatMap: if you have a random number generator, you can have a random generator for any type you want!


Last words

To sum up, this is what I’ve taken with me from DO iOS University:

  • flatMap rocks! It’s more than just a useful function — as Daniel said, you can also think of flatMap as a design pattern.
  • Naming, naming, naming. Name everything in your code appropriately so it’s clear — not only for you, but also for the other people working with it.
  • Take advantage of structures that can store closures as variables. This is not a straightforward approach for those coming from a OO background, but it can help you avoid unnecessarily writing tons of code (and bugs!) when doing the work.
  • Write generics after the fact. Focus on getting the work you need done first — and write a nice, reusable abstraction for it after you’re done.

That’s it! If you’d like to know more about DO iOS, check it out here.