Like many Mac and iOS developers, I haven’t yet made the jump from Objective-C to Swift, nor will I make it in the near term.
Just like most other developers, I am slowly putting a little bit of Swift code into new projects to get a feel for the language and I’m experimenting with its new features to be ready when it becomes a more practical proposition.
At the moment, I’m trying to come to grips with “optionals“, a construct that I have never seen in any language I’ve used before.
Essentially, “optionals” are shoeboxes that may or may not contain a value. You can find out whether they contain a value and if so, you can get the value out by “unwrapping it” and if it contains a mutable type, you can change it.
This replaces the C-style pointers that may either point to the data belonging to an object (in the largest sense of the word) or have a “nil” or “NULL” value that signifies “no object”.
Optionals are a clear improvement on null pointers or nil types in at least two significant ways:
- Optionals eliminate a whole class of common bugs by forcing developers to think about “what happens if this method does not return a valid object/ value”
- Optionals help make Swift code faster by making it easier and safer to optimize code; optimizers do not have to worry about edge cases where the value may be empty.
The price for those two advantages seems very steep, however.
First, not dealing with potentially invalid objects being returned prevents the code from being compiled in the first place. You cannot not deal with the problem.
Secondly, optionals require developers to learn a host of new concepts. This may be a good thing in the long term, but it will slow adoption as you can’t just dive into the language without understanding optionals and you have little hope of ever having come across them before (unless you are beta testing Rust that is).
Using optionals is also really, really cumbersome. Unwrapping each and every optional makes even trivial programs balloon to many times their normal size. This is why Apple has thoughtfully added a lot of syntactic sugar to make the medicine go down that much more easily.
This syntactic sugar (chaining, etc.) makes your code smaller and cleaner and you can almost forget that it is there at all when you read it. Unfortunately that isn’t the case when you write the code and it does also add a whole layer of complexity making Swift harder to learn and understand.
Optionals also do not map onto anything in Objective-C and as there are no native libraries yet, “implicitly unwrapped optionals” abound and feel very much like a backwards-compatibility hack and a solution to a problem you wouldn’t have without optionals.
So there is a price to be paid for beauty, performance and safety.
That needn’t be a show stopper. You get nothing for free in life and the result may be worth the effort.
Unfortunately, when I’m actually using Swift, I find that I don’t seem to be getting much value out of optionals.
Firstly, there’s the fact that now instead of trying to express what I want to do, I find myself wondering about whether I need to unwrap my optionals? and what might be the best syntax for expressing the unwrapping? where should it be done? now or later? Why does this code not compile?
Admittedly with a little bit of practice, this will probably largely go away. There will always be a cognitive overhead associated with it, but it will that overhead is likely to get smaller with time. I certainly won’t miss having to check the location member of the return value of rangeOfString: for NSNotFound.
The real killer for me is, however, that I now find all those places in my code where I need to think about what to do when the optional is empty .. and I find that there’s not much to be done.
What can you do when you are loading an image from your app bundle and it is not there? There only seem to be two options:
- raise an exception, crash or display an alert that the user can’t do anything about
- do nothing
Crashing is great, because now there’s a crash report being generated and I know that something has gone wrong in the production version and I can work out what the cause is and correct it. Crashing early is best practice. It certainly beats crashing later.
Doing nothing might be okay in many situations, because it might not be so bad. It is in fact what Objective-C does most of the time in this situation. It just ignores the problem, the application keeps running (but not necessarily working) or it simply crashes somewhere down the line.
Herein lies the problem with optionals. They force you to deal with potential problems that you can’t do anything intelligent about. Worse, because of the absence of an exception mechanism you need to deal with exceptions in situ, polluting your beautiful code with non-sensical error handling code.
So, what I’m wondering is: “Is there any value in explicitly dealing with problems that you can’t do anything about?”