It’s time to remove some magic regarding operators. It’s also time to stop fearing them and start treating them as just another tool. They have their place and they are very useful for some tasks.
Operators we know
In the early days of education we are toted operators. I’m 100% sure that when I say that I have in mind addition, subtraction, multiplication, and division. You will have some graphical symbols associated with those arithmetic operations.
+ − ✕ ÷
Use of those operators is very “intuitive”. I use quotation marks here because this intuition is instilled in our youngest years. So that’s why they have a familiar shape. We know the meaning and without any problem we can infer their behavior.
2 + 2 = 4 3 − 2 = 1 2 ✕ 4 = 8 4 ÷ 2 = 2
Unfortunately not all of those operators are so easy to type on a keyboard. So while typing code some of them got a new ASCII shape that as closely as possible resembles original shape.
2 + 2 // 4 3 - 2 // 1 2 * 4 // 8 4 / 2 // 2
World without operators
Let’s see how would the same operations looks if you used plain functions.
add(2, 2) // 4 subtract(3, 2) // 1 multiply(2, 4) // 8 divide(4, 2) // 2
Not bad. One could say that it reads nicely. But will it also scale so nicely? Let’s check it on a bit harder example. I promise it won’t be a much harder one:
// (2 + 8 / 2) * 4 + 1 add(multiply(add(2, divide(8, 2)), 4), 1)
The readability of this example just went out the window. At the first glance of an eye you have to figure out what will happen in what order. The same goes for arithmetic operators but it’s easier. The intention of the programmer writing code is more clear in the operator case.
It’s easier to modify the operator version of the code. Thanks to the more compact code it’s easier to add or remove extra parts of the computation. With functions just handling the brackets can be challenging.
In my work I had met many times with the opinion that you should not use custom operators. To be honest I must admit that when overused they can make code look worse not better. But… there’s always some but… you can say the same thing about any new and unfamiliar API. What do really method named
Names in programming are very important. When we do not know anything about a method then based on the name and experience we can guess what should happen. There is no guarantee but at least we have some associations. Good or bad that is not so relevant but there is something to hold on to. To put it simply on one wants to feel like an idiot.
A well-designed operator can have some associated meaning attached to it. What do you think when you see
⛔️? Those symbols thou not ideal have some relations to things we already know. They are more universal than writing and some shapes are more recognizable than others. But it boils down to what we already know or not.
Another hoop that developers need to jump is a bit of magic on how operators actually work. Normally no one has trouble with calling a method on an object. But when you see one custom operator than the whole city goes bonkers.
Anatomy of an Operator
That might come as a shock but operators in Swift are functions. Only one special thing about them is that they have a sugar syntax for calling them. The rest is basically the same.
let add: (Int, Int) -> Int = (+) let subtract: (Int, Int) -> Int = (-) let multiply: (Int, Int) -> Int = (*) let divide: (Int, Int) -> Int = (/)
Swift treats functions as first-class citizens. In practice it means that you can store function in a variable and use it as any other regular variable. Each time the type of this function is the same. It’s a function that takes two arguments of type
Int and returns
-> a value also of type
Int. Whole signature looks like this
(Int, Int) -> Int. At the end of definitions of those functions we assign corresponding operator eg.
= (+) for
add. I must wrap the plus operation in parentheses
() so Swift will know that I want a reference to the function not to actually apply it. That was how I have defined those function in an earlier example
add(multiply(add(2, divide(8, 2)), 4), 1).
Going a step further I can rewrite it using those operators as functions. As the name is just yet another reference to yet another reference to a function I will get this:
(+)((*)((+)(2, (/)(8, 2)), 4), 1) // 25
Good luck with deciphering what’s going on there. But this just proves the point that we need this special syntax in Swift so we can simply write
(2 + 8 / 2) * 4 + 1.
We will start our journey about operators. We will look on their shapes and how they work. I will do my best to show you how just the right amount of operators allows to be more declarative than imperative in writing style.
So to not to leave you in a state of limbo until we meet agin here are some other resources. I’m very inspired by them and I hope you will be to.
A lot of functional programming concepts can be found there. I must admit that there I saw a lot of them for the very first time and went down the rabbit hole straight to Haskells Operators Zoo. It’s not a free resource but some parts are free and definitely worth seeing them.
- Swift 5’s brand new Result Type – iOS Conf SG 2019
Daniel Steinberg talks about
Resulttype and introduces some operators along the way.
Swift library with the implementation of some of the less known operators.