This episode is for subscribers only. To access it, and all past and future episodes, become a subscriber today!
See subscription optionsorLog inSign up for our weekly newsletter to be notified of new episodes, and unlock access to any subscriber-only episode of your choosing!
Sign up for free episodeLast time we revisited a topic that we know and love: algebraic data types. We explored how in the Swift type system Swift, multiplication manifests itself in product types, like structs and tuples, and addition manifests itself in sum types, like enums. We saw that, just like in algebra, addition and multiplication, and structs and enums are just two sides of the same coin: no one is more important than the other. And we saw that many of the things we love about structs do in fact have a corresponding feature on enums, and vice versa.
However, we also saw that product types definitely see some favoritism over enums in the language design. We saw this especially in how Swift gives us anonymous structs in the form of tuples, but there is no corresponding anonymous enum type. We theorized how it might look and we may even have it in the language some day because it’d be a really nice tool to have.
Now anonymous sum types isn’t something we can fix ourselves, but there are other examples of struct favoritism that we can correct by bringing enums up to the same level.
👋 Hey there! Does this episode sound interesting? Well, then you may want to subscribe so that you get access to this episodes and more!
While we’ve defined the get
s of our enum properties, we haven’t defined our set
s. Redefine Validated
’s valid
and invalid
properties to have a setter in addition to its getter.
We can redefine valid
with the following setter.
var valid: Valid? {
get {
guard
case let .valid(value) = self
else { return nil }
return value
}
set {
guard
let newValue = newValue,
case .valid = self
else { return }
self = .valid(newValue)
}
}
While we must safely unwrap a non-optional newValue
to reassign self
, we also test that the current self
is valid
before doing so.
The invalid
property is the same boilerplate but uses the invalid
case.
var invalid: [Invalid]? {
get {
guard
case let .invalid(value) = self
else { return nil }
return value
}
set {
guard
let newValue = newValue,
case .invalid = self
else { return }
self = .invalid(newValue)
}
}
Take the valid
setter for a spin. Assign Validated<Int, String>.valid(1)
to a variable and increment the number using the setter.
There are a few ways of doing this! First, let’s assign a mutable variable.
var v = Validated<Int, String>.valid(1)
We can map
over the optional Int
returned by the valid
getter.
v.valid = v.valid.map { $0 + 1 }
Or we can optionally chain with the +=
operator.
v.valid? += 1
An episode dedicated to property access on structs and how key paths can further aid us in writing expressive code.
Key paths aren’t just for setting. They also assist in getting values inside nested structures in a composable way. This can be powerful, allowing us to make the Swift standard library more expressive with no boilerplate.
A proposal has been accepted in the Swift evolution process that would allow key paths to be automatically promoted to getter functions. This would allow using key paths in much the same way you would use functions, but perhaps more succinctly: users.map(\.name)
.
This site is a cheat sheet for if case let
syntax in Swift, which can be seriously complicated.