

Exhaustive Properties with Tuples
source link: https://www.tuicool.com/articles/hit/AziUfq6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

One thing I really love about Swift is its exhaustive switch
statement. As programmers, we live with a creeping dread of change and the unforeseen consequences it can wreck on our carefully calibrated computations. And switch
is an excellent hammer with which we can nail down at least one future loose end.
If we have code that calculates how many spaces a character can move in a game, for example:
enum Character { case elf, dwarf, human } func spaces(for character: Character) -> Int { switch character { case .elf: return 7 // Spritely! case .dwarf: return 3 // Short legs; big heart. case .human: return 5 // Even Steven. } }
…and we later add a dragon:
enum Character { case elf, dwarf, human, dragon }
No problem! The Swift compiler let’s us know there’s a spot where we haven’t considered what to do with dragons, and won’t let us continue until we deal with it:
func spaces(for character: Character) -> Int { switch character { Switch must be exhaustive //... } }
This future-proofing is such a comfort to me, I cram everything I can in switch
es. But sometimes it’s just not feasible. Let’s look at a simple person model. It can hold a firstName
and/or a lastName
, and put those together in a description
:
class Person: CustomStringConvertible { var firstName: String? var lastName: String? var description: String { return [firstName, lastName] .compactMap { $0 } .joined(separator: " ") } }
It also has utility methods for letting us know if it has a full name, and for returning either the first or last name, whichever is present, for more informal modes of address:
extension Person { var isFullName: Bool { guard firstName != nil, lastName != nil else { return false } return true } var firstOrLast: String? { return firstName ?? lastName } }
This all looks fairly straightforward, but I’m terrified
. What if later we add a middleName
property? Nothing will break, yet everything will be wrong. It will be up to whomever implements middleName
to search for all the places it might be relevantand incorporate it.
How can we get switch
-like exhaustiveness when dealing with the properties of a type? The first step (and our first clue) is to treat the properties as a set. What if we put them in a tuple?
class Person: CustomStringConvertible { var name: (first: String?, last: String?) }
Accessing the properties of Person
is still painless:
let myPerson = Person() myPerson.name.first = "Deedlit"
And we can now rewrite description
and isFullName
in terms of the tuple:
var description: String { let (first, last) = name return [first, last] .compactMap { $0 } .joined(separator: " ") } var isFullName: Bool { guard case (_?, _?) = name else { return false } return true }
But firstOrLast
, which only depends on a first and last name and doesn’t care if we ever add more, can reference the values of the tuple directly:
var firstOrLast: String? { return name.first ?? name.last }
Now what happens if we add a middle name?
class Person: CustomStringConvertible { var name: ( first: String?, middle: String?, last: String? ) var description: String { let (first, last) = name tuples have a different number of elements // ... } } extension Person { var isFullName: Bool { guard case (_?, _?) = name else { tuples have a different number of elements } // ... } var firstOrLast: String? { // ... } }
description
and isFullName
are both flagged with compiler warnings, firstOrLast
just keeps on trucking, and I cut my antacid budget by 4×.
So, to be clear, habitually shoving all properties into a tuple won’t scale well. But it is a useful tactic to employ when dealing with models, mocks or anything that has:
- naturally constrained state,
- logic that depends on the shape of said state, and
- a strong affinity towards change in the face of shifting requirements
When we are confronted with such beasts, we can save our future-selves some consternation by popping properties into tuples.
1:
Think of all the places extension Person
could live if you really want to break out in a cold sweat.
2:
Though, outside the forced reality of a blog example, the Person
model should be a struct
, its name
should be a let
, and everything immutable. Still, it’s good to know mutable tuples are a thing.
3:
The (_?, _?)
in isFullName
might look odd. It is a combination of the Wildcard Pattern
and the Optional Pattern
. It means, roughly, “a tuple of two non-optional things.” We’ve talked about the Optional Pattern before,over here.
Recommend
-
46
Let's say we all love ice cream. So we define the following enum in TypeScript: enum IceCreamTaste { awesome, meh, dunnoYet } And now we want a switch that returns an answer to your friend's...
-
39
Xubuntu 19.04 “Disco Dingo” is just around the corner. It features numerous updates and an updated snapshot of Xfce 4.14 development. If you want to see all the changes, you’ve come to the right place! What’s N...
-
43
readme.md
-
49
If you are looking for ways to reduce your Xcode project’s build time i.e. improve build performance, then you are at the right place. I chose this topic since I haven’t come across any blog or article that has an exhaust...
-
13
Never patterns, exhaustive matching, and uninhabited types (oh my!) Aug 13, 2018 One of the long-standing issues that we’ve been wrestling with in Rust is how to integrate the concept of an “uninhabited type” – that is,...
-
11
Translation Validation of Bounded Exhaustive Test Cases – Embedded in AcademiaThis piece is jointly authored by Nuno Lopes and John Regehr. Compilers should be correct, but it is...
-
12
Contributor Nadrieril commented
-
13
Collaborator camsteffen ...
-
10
Using Tuples in C# to Initialize Properties in the Constructor and to Deconstruct Your Object Recently I was asked by a developer what this code block here actually is: public Friend(s...
-
6
Copy link Collaborator
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK