Figure

A blog about Swift and iOS development.

Readable Swift: The Curious Case of Not

Where's the Not?

Before a thing can be read, it must be seen. And this is a problem with Swift's1 "not" operator: !. It's comparatively thin so it doesn't leave a lot of ink on the page. And unlike a . or ,, it's also tall, so it isn't able to use its negative space to stake out territory.

Compare:

foo.bar.baz
foo!bar!baz

(.many, .unique, .books)
(!many, !unique, !books)

The . pops out as a "nonletter". The ! blends together with whatever's around it. Which makes it non-ideal for a "reevaluate this entire expression as the opposite of whatever it was" operator.

Surprisingly, the rise of boutique "programmer's fonts" hasn't really helped us out here. Everything from Courier Prime to Source Code Pro2 renders ! more or less the same; an undifferentiated thin line. Even Hoefler&Co's Operator, which goes out of its way to be ugly in the name of readability, toes the line when it comes to the humble !.

But where $600 typefaces fail us, we can oft find salvation in unicode and emoji:

prefix operator ❗️

prefix public func ❗️(a: Bool) -> Bool{
  return !a
}

I'm not going to claim this hasn't been a controversial change for some of my teams. But it's indisputable it stands out:

foo!bar!baz
foo❗️bar❗️baz

(!many, !unique, !books)
(❗️many, ❗️unique, ❗️books)

Of course, a ❗️ is a little more difficult to type than a "!". But that's not a bug, it's a feature! Because…

The Only Winning Move is Not to Not

Let's look at a simple conditional.

if homer.isLickingToads { ... }

This is the very definition of readable. Why? Because (english-speaking) brains are highly adapted to parse english and this reads like english: "If Homer is licking toads…".

Now let's look at its negation:

if ❗️homer.isLickingToads { ... }

Hmm. This is a little less readable3 because it doesn't parse quite right. "If not Homer is licking toads…" ultimately makes sense, but only because we disengage our language center and engage our logic circuits to eval it. This creates friction.

Now, it's really important to note we write all our code in "logic mode". So typing out ❗️homer.isLickingToads is the most natural, easiest thing to do at coding time — even though it's (slightly) more difficult to read afterwards.

This asymmetry is important to call out because it's the opposite of useful. We write code only once but read it many times thereafter. If we have to introduce a burden, we want to shift it to the writer, not the reader.4 So we would like this to read:

if homer.isNotLickingToads { ... }

Our linguistic brains parse this just fine. And that troublesome ❗️ has vanished altogether!

On the down side, our logical brains now need to write more code. How much more? Just:

extension Homer {
  var isNotLickingToads: Bool { 
    return ❗️isLickingToads 
  }
}

That seems a totally worthwhile tradeoff.


1: Along with every other common programming language… though interestingly this is one of those cases where C-based languages diverged from their ALGOL-roots. ALGOL and it's non-C derivatives use NOT.↩︎

2: Which is the best and is totally what you should be using.↩︎

3: And if you feel there's nothing wrong with this, please substitute your own arbitrary number of parentheses and ||s until it becomes scary.↩︎

4: It's probably also worth pointing out that, until we make this shift, code review is either sisyphean or useless.↩︎


Hit me up on twitter (@jemmons) to continue the conversation.

Latest | Archives