Syntactic Diabetes

When learning a new language (be it a natural or programming language) one of the big first stumbling stones is getting a grasp of the basic grammar rules. For languages with a very simple and regular grammar this is less of a deal. But for others, such as German, it can be quite intimidating.

For a natural language there are basically three ways of encoding information: A language could encode information by the use of:

  1. Patterns: An elaborate and rigid set of grammatical rules, allowing for an over all smaller set of irregular (and mostly unique) word inflections.
  2. Specialization: An exhaustively large set of irregular (and mostly unique) word inflection patterns, making up for the lack of a more complex and rigid grammar.
  3. Verbosity: An extensive set of function words making up for the lack of possibly both, a more expressive grammar, as well as inflection, at the cost of increased redundancy in the language.

What a languages lacks in one department, it usually makes up in another. A common consensus among linguists is that all languages are equally complex. (Another take is that current linguistics simply lacks a coherent theory of complexity and by that a useful, objective measure of the latter.)

Things are similar for programming languages, with the spectrum probably laying somewhere between Haskell (for its highly expressive type-system), Perl (for its infamous over-use of sigils) and AppleScript (function words everywhere).

Swift probably finds itself somewhere in the middle, leaning somewhat in the direction of Haskell.

In the context of programming one usually talks about practical expressive power when comparing languages.

Comparing an implementation of the Sieve of Eratosthenes in Objective-C …

… with an equivalent one in Swift …

… one cannot but notice how the new cool kid on the block — Swift — is getting away solving the same problem with much less noise and fewer leaky abstractions, than old-timey Objective-C.

If Swift had a splitFirst() function on Array, as Rust does for Slice …

… one could reduce it even further to …

… eliminating the unsafe array access.

Swift manages to provide such a boost in productivity and expressivity substantially thanks to the adaption of well-established features common to many functional programming languages such as pattern matching, higher-order functions, some kind of type-classes, algebraic data types, and last but not least a strong type-system.

Thanks to this tuples, enums and closures have become part of Swift’s most used and loved bells & whistles.

Tuples and enums form the foundation of pattern matching in Swift.

Pattern matching in Swift are generally marked by the prefix case … (Swift does not use pattern matching everywhere, like other FP languages do, so the case … is necessary to tell the Swift compiler when use it):

Like, for example, when one wanted to make use of the result of the splitFirst() function:

But wait, that’s far from idiomatic Swift, isn’t it? One would most likely write something like this instead …

… and have it lowered to the former by the Swift compiler.

Syntactic Sugar

Swift makes use of this so-called syntactic sugar to make working with it more convenient and to allow for progressive disclosure:

Swift’s if let foo = bar { } is syntactic sugar for the more verbose if case let .some(foo) = bar { }, allowing one to learn to use T? while sparing one a trip down the rabbit hole of learning what an enum is and how it works.

Interestingly up until Swift 2.x the following code actually worked, as Swift was simply pattern matching against .Some(_):

With Swift 3.0.0 this turned into the rather misleading error:

Error: enum case ‘Some’ not found in type ‘Maybe<Int>’.

… which with Swift 3.0.1 was then changed further into the current more useful error:

Error: initializer for conditional binding must have Optional type, not ‘Maybe’.

Another example of syntactic sugaring is for element in sequence { … }, which gets lowered, or may I say desugared, to:

Or take returning an instance of T from a function (_) -> T?:

… which gets lowered to:

And things get even funkier with indirect enums:

… which gets lowered down to something like this:

… with Box<T> denoting a heap-allocated instance of T.

While at first all this doesn’t look like much of an improvement from the type declaration alone, the benefits become quite obvious when trying to actually use the type:

The sugar-free version of this would look something like this (assuming Swift could pattern match on classes):

Apart from a LISP joke hiding in plain sight it should be uncontroversial to state that the former does much better in conveying one’s intentions.

Syntactic sugar makes complicated things look simple.

Syntactic Salt

While syntactic sugar aims to make it easier to write good code, syntactic salt aims to make it harder to write bad code.

As such Swift requires one to add a break-statement to an empty switch-block, even though those break by default in Swift.

Or take the requirement to append override … to a function declaration if one intends to override it.

The compiler has all the information it needs to figure these things out on its own. But as both things were cause of many bugs in languages that came before Swift, its authors decided to make things more explicit.

Syntactic Saccharin

Syntactic saccharin refers to syntactic sugar that, while having good intentions, fails to do its thing, possibly making things even worse than without it.

One pattern that causes syntactic sugar to turn into syntactic saccharin is when the former, while trying to make things simpler, ends up introducing its own exceptions which now need to be learned in addition to the things it tries to simplify.

Assigning a non-optional value to an optional one works for both its de-sugared form = .some(42), as well as its sugared one: = 42:

This is great for progressive disclosure, as it allows new users to get up to speed with Swift rather quickly.

The downside of syntactic sugar is that it can give users a wrong image of a language’s semantics and in the worst case a false sense of safety.

As such a new user of Swift might deduce that any occurrence of type T? can always be filled with values of type T, as that’s what’s what clearly appears to be happening in the snippet above.

Reading through the official book on Swift one would further come across this:

You can put parentheses around a named type or a compound type. However, adding parentheses around a type doesn’t have any effect. For example, (Int) is equivalent to Int.

So, one would come to the conclusion, that the above could also be expressed like this (and indeed, it can):

So, if this works with a single element, one might reason, then doing it with two shouldn’t be too far off, or should it?

Error: cannot express tuple conversion ‘(Int, Int)’ to ‘(Int?, Int?)’.

While totally making sense from the type system’s point of view it produces cracks in the thin facade the syntactic sugar had set up to hide the nitty gritty details of Optional<T> and enum in general.


In Swift .enumerated() returns a sequence of pair tuples with named fields (offset: Int, element: Base.Element), where offset represents a consecutive integer starting at 0, and element represents an element of the sequence:

To destructure a tuple with named fields one provides local binding names for each field label:

Alternatively one can also just destructure it as if it was an unnamed tuple:

The latter however lacks robustness against re-ordering of named fields in the tuple:

On a side note: By allowing indexed access to named tuples Swift essentially makes every change of field order a breaking change, due to leaky abstractions, which somewhat defeats their whole point of having tuples with supposedly order-invariant named fields.

Yet, this indexed access is what allows one to destructure pair in-place like so:

Things are similar with enumerating Dictionary:

So, what if one wanted to enumerate the key-value pairs of a dictionary by their indices?

No problem. But what if one wanted to destructure pair?

Error: cannot express tuple conversion ‘(offset: Int, element: (key: Int, value: String))’ to ‘(Int, (Int, String))’.

All of a sudden Swift forces one to utilize the named variant:

So now — due to an exception to the norm — one is forced to use named destructurings in some of the places, which a reader now might falsely expect to mean to convey a certain intent, albeit just being there out of sheer necessity.


In other cases syntactic sugar simply ends up exchanging a single slightly less convenient yet congruent language pattern with a bunch of subtly different alternatives, which now have to be learnt regardless of personal preference (after all one tends to read much more code of other people, with likely different stylistic preferences, than code one had written on one’s own):

What does this code end up printing?

What if Swift had instead opted (which at one point it had) to only provide a single and slightly improved version of if case let …?

Swift’s let foo = bar is kind of weird in that it completely changes its behavior when prefixed by if … or guard … and at times results in awkward looking code like if let foo = foo { … }.


For the next few snippets consider array to be of type [(A, (B, C))].

Enumerating it is straight-forward with both for … in and .forEach(…):

And given that “(T) is equivalent to T”, as we learnt from the book, we could just as well write things like this:

Or we could destructure it:

Now one might remember that parentheses are optional for the (…) in of closures and attempt to do the same with for … in, like so:

And would promptly be confronted with an error for the former:

Error: expected ‘in’ after for-each pattern.

Similarly one might decide to destructure b even further, changing (a, b) to (a, (b, c)):

At which point one would be told for the latter that:

Error: unnamed parameters must be written with the empty name ‘_’.

Which appears to be an awkward way of swiftc telling one that “nested destructuring is not (yet) implemented for closure parameters”.


Another common form of syntactic sugar, or should I say saccharin, is the support for implicit self. within a type’s associated methods.

There are just way too many ways in which removing/renaming a previously shadowing local variable name can introduce nasty and hard to spot bugs. And the fact that there still is no support for refactoring in Swift’s tooling, thus forcing one to reach for poor-man’s refactoring tools such as find & replace, just adds insult to injury.

Alas this ship has sailed.

A syntactic sugar that does not cleanly apply to all possible use-cases or does not provide proper re-sugared error messages is at risk of effectively turning into syntactic saccharin.

While Swift tries to reach expressiveness through universal patterns, allowing for simpler language semantics, the use of leaky or fragmented syntactic sugars at times ends up forcing the user to learn both, the core language, plus all those sugars coming along with, and most notably, their numerous exceptions*. Further more, lifting library types up into language items (as seen with T?) for the sake of sugaring always risks turning user-land types into 2nd-class citizens of the language, constraining its extensibility.

*In case you were wondering what the hell all this had to do with the intro alluding to the hurdle of learning a foreign natural language.