September Writing Challenge, Post 6: My Favorite Trick With Swift Enums

I have to credit TJ Usiyan with enlightening me about this use of Swift enums with associated values. Thanks for that, TJ!

If you’ve used Cocoa APIs much, you’ve seen the pattern of using an NSError pointer as an “out” parameter. In Objective-C:

Or in Swift:

I am not a fan of this pattern, for two reasons (or three, if you count aesthetics).

First, the use of an out parameter, like all side effects of processing, breaks the idea of a function. Arguments should go in, a result should come out, and there should be no mysteries about what happened in between, and you should have one place to look for the output. This business about putting an important chunk of output state (the existence and kind of error plus its metadata) out-of-band seems, at best, to be a misguided attempt to preserve the purity of the intended, happy-path output type.

If that’s all we cared about, we could fix it like so (sticking with Swift from here on):

But this also suffers from the second problem with the pattern: You have no idea of the legitimate output states (unless there is documentation, and it is accurate, and you have read it – a triply-nested conditional). It’s pretty likely, however, that the legitimate output states are a subset of the possible output states.

Intuitively, we think we should get a record OR a parsingError, but not both. And that’s kind of a pain, because we still have to check one or the other for being nil, and we should probably check the other one just to be safe, and that involves a lot of conditionals, and as I’m fond of saying, unnecessary conditionals are bad, mmmkay?

But hey – what if your intuition is wrong? What if you could have both fields in that struct filled? Maybe the record could be parsed, but only incompletely, so you get back a partially-filled record and a parsingError letting you know that there’s stuff missing.

And what if both the record and the error are nil? That sounds insane, but I’m sure someone could contrive a scenario where that might be legitimate. Somewhat easier to concoct is the scenario where both come back nil because of a bug.

This pattern involves a ton of trust – the caller trusts that the code puts out only intended, documented combinations of record and error, despite having no assurance at the language level that this must be so. The writer of the parse() function is trusting that the caller is going to correctly handle all specified output states, and never force-unwrap a nil record object or what-have-you.

Well-designed systems obviate trust. (Ever signed a contract?)

So what is The Better Way™?

Ponder that a moment… This makes it so much harder to screw up. To wit:

Are you worried about whether a bug will cause both fields to come back nil? Don’t sweat it – it can’t happen. You have exactly two possible states, and the associated objects for both are non-optional. What about getting an error and a result back? Nope, can’t happen. And if you wrap your handling in a switch statement, you are forced to handle both legitimate states. (That is, unless you include a default case, but I’m going to put a stake in the ground right now and say that the use of default when switching on a Swift enum is a code smell.)

But what about that case where the record was parseable, but only incompletely?

…and now you can get back your partial record with an error telling you whether the parsing was incomplete due to a short record or a missing delimiter or what.

And calling code that switches on the result must account for that case somehow.

Another thing I’ve seen – and this can happen at serious companies run by grown-ups – is a poorly designed record format where the field delimiters are in-band characters that can be mistaken for record data, and multiple interpretations of the record are possible. Can we handle that case?

…and now you can pass back a set of possible interpretations of the ambiguously-formatted data and let the calling code ask the user or take a guess or whatever to select the right one.

I have my beefs with Swift, but enumerations with associated values is a place where Swift gets it so very, very right. You can define system states in a way that is both complete and exclusive – you can make illegal states unrepresentable, to borrow a phrase. No more nil checks, no more wondering what the legit outputs are, no more worrying (or anyway, less worrying) about whether a junior dev calling your code will handle all the cases. The compiler has you covered – so now you can spend your effort on the real, value-adding stuff.

Comment fodder: Do you have any other cool examples of making illegal states unrepresentable? No need to limit it to Swift – I’d love to learn more about how other languages do it!