Swift: Optionals Without Conditionals

Hello there my sweet fellow developers,

After I shared my innocent posts about avoiding duplication, iflets and guards being unreadable and how bad implicit optionals and force casts are I received a massive ~butth…~ feedback from the community of fellow reddittors. Some of them even claimed that I am incompetent, inconsiderate towards others, rapist and made calls to genocidal actions. Aside from those overexaggerations, what I noticed is that most of comments either labeled Optional as the entity used in conditional statements or the entity, that should lead to crashes. Another important stance I heard as an excuse for avoiding language features used for deduplication, is that the code would become less comprehensible.

I was a bit puzzled to say the least, as conditionals is the most difficult and lengthy way to use the Optionals. That’s because we have loads of functions in the standard library used specifically for optional processing. I’m not even mentioning SwiftZ, SwiftX and the likes, which turn an already powerful instrument into something truly amazing. And yep, they are incomprehensible in case you don’t know the basics behind them, as the methods for Optional handling originate from maths. But that’s not the reason to avoid them, that’s the reason to learn something new and improve.

So, lets start from the beginning. Optional, in case you don’t know, is the type, that represents availability or absence of value. As simple, as that.

Now, lets take a look some conventional Swift code written by the average dev:

Disclaimer: This is a sample code outlining the approach, same applies to other snippets. Pardon me, all my readers, who do actually understand that. You’d be shocked at how many people didn’t get, that my articles describe general approach, not specific use cases.

It’s pretty obvious, what the function does, but I’ll still explain. You pass two characters as bounds and receive the array of 1 character long strings in the range between those two characters.

The only thing I wrote in here, that is rarely used is charToUInt, as I wanted to avoid a bit of duplication. charToUInt is your typical function written in a closure way, as declaring such a small and easy to grasp function using func is a bit of an overkill in my opinion. You can read more about that kind of deduplication in my article about first order functions.

Most of you, my dear readers, have either written or seen such code. But have you ever thought, that you could improve that code, make it more extensible flexible and less lengthy?

Now, before we proceed any further, I strongly suggest reading this awesome article about Monads, Functors and Applicatives. While I won’t be using all of the concepts of the article to the fullest with SwiftZ, you’ll still do yourself a favor, as you would surely become better SDE. Moreover it will simplify reading my article.

As a quick reference:

  • Monads implement func flatMap(f: (T) -> M) -> M, where M is Monad, wrapper around some value T;
  • Functors implement func map(f: (T) -> T) -> F, where F is Functor, wrapper around some value T.

So, with all that knowledge in mind, lets take a look at how we could apply that knowledge to conditionalCharacterStrings:

func toTuple is a mere hack, as tuples in Swift are really limited as of now. They can’t conform to protocols, they can’t have functions of their own. In real world, the code generation approach used in SwiftZ for Kinds and Curry is preferred. Sadly, that’s the only way as of now. This function just returns a tuple of 2 first elements of Array, if the Array count is 2, otherwise it returns nil.

As for the return statement, it essentially does the same, as the previous example, with a few quirks:

  • The result of processing is optional, so we default to empty result in case we couldn’t transform inputs into UInt16 or there’s no min value in bounds;
  • bounds.toTuple().map expects a function, that returns a value to be wrapped in Optional by map itself.

The solution is hardly comprehensible in case you meet such approach for the first time. On the other hand, it is much more readable, when you are used to the approach, and is extensible and robust. It clearly expresses the intentions in terms of data processing. It avoids all the unnecessary duplication. The solution as a whole is a processing chain with no early fallbacks. The only unsafe code is isolated in a separate function, which needs extensive and exhaustive testing and could then be reused everywhere. It could even be written as a single chainable return statement, if swiftc wasn’t too dumb to solve the expression in a reasonable time.

So, it’s basically, up to you, if you would risk trying the approach. If you do, you’ll be amazed at how simple and short your code becomes, how simple it is to handle, modify and abstract out in case duplication arises.

That’s all, folks. Have a great day and stay DRY, no matter, where you are.

Comments