This episode is for subscribers only. To access it, and all past and future episodes, become a subscriber today!See subscription optionsorLog in
Sign 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 episode
We’ve now spent a couple episodes exploring “functional setters”: functions that allow us to build up expressive, immutable data transformations from small units. We’ve explored how they compose together in surprising ways to let us make changes to deeply-nested values: changes that are generally cumbersome to make. And we’ve leveraged a wonderful and unique Swift feature, key paths, to pluck setters out of thin air for properties on our classes and structs.
Setters are an incredibly powerful and broadly useful tool, but the current functions we’ve written have some rough edges when it comes to using them. They’re also not the most performant things in the world: because setters are immutable, they create copies of their values every step of the way. Today we’ll smooth out those rough edges and explore how we can use Swift’s value mutation semantics to make things more performant.
We previously saw that functions
(inout A) -> Void and functions
(A) -> Void where A: AnyObject can be composed the same way. Write
^ in terms of
AnyObject. Note that there is a specific subclass of
WritableKeyPath for reference semantics.
Our episode on UIKit styling was nothing more than setters in disguise! Explore building some of the styling functions we covered using both immutable and mutable setters, specifically how setters compose over sub-typing in Swift, and how setters compose between roots that are reference types, and values that are value types.
concat as single-type composition, but this doesn’t mean we’re limited to a single generic parameter! Write a version of
concat that allows for composition of value transformations of the same input and output type. This should allow for
prop(\UIEdgeInsets.top) <> prop(\.bottom) as a way of assigning both
bottom the same value at once.
Define an operator-free version of setters using
concat from our episode on composition without operators. Define an
update function that combines the semantics of
with and the variadic convenience of
concat for ergonomics.
In the Haskell Lens library,
set are defined as infix operators
.~. Define these operators and explore what their precedence should be, updating some of our examples to use them. Do these operators tick the boxes?
We open sourced the Overture library to give everyone access to functional compositions, even if you can’t bring operators into your codebase.
Stephen spoke about functional setters at the Functional Swift Conference if you’re looking for more material on the topic to reinforce the ideas.
Conal Elliott describes the setter composition we explored in this episode from first principles, using
Haskell. In Haskell, the backwards composition operator
<<< is written simply as a dot
., which means
g . f is the composition of two functions where you apply
f first and then
g. This means if had
a nested value of type
([(A, B)], C) and wanted to create a setter that transform the
B part, you would
simply write it as
first.map.second, and that looks eerily similar to how you would field access in
the OOP style!