Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Introduction
Last 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.
Subscribe to Point-Free
Access this episode, plus all past and future episodes when you become a subscriber.
Already a subscriber? Log in
Exercises
While we’ve defined the
get
s of our enum properties, we haven’t defined ourset
s. RedefineValidated
’svalid
andinvalid
properties to have a setter in addition to its getter.Solution
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 reassignself
, we also test that the currentself
isvalid
before doing so.The
invalid
property is the same boilerplate but uses theinvalid
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. AssignValidated<Int, String>.valid(1)
to a variable and increment the number using the setter.Solution
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 optionalInt
returned by thevalid
getter.v.valid = v.valid.map { $0 + 1 }
Or we can optionally chain with the
+=
operator.v.valid? += 1
References
Getters and Key Paths
Brandon Williams & Stephen Celis • Monday Mar 19, 2018An 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.
SE-0249: Key Path Expressions as Functions
Stephen Celis & Greg Titus • Tuesday Mar 19, 2019A 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)
.
How Do I Write If Case Let in Swift?
Zoë SmithThis site is a cheat sheet for if case let
syntax in Swift, which can be seriously complicated.