Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Introduction
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.
Subscribe to Point-Free
Access this episode, plus all past and future episodes when you become a subscriber.
Already a subscriber? Log in
Exercises
We previously saw that functions
(inout A) -> Void
and functions(A) -> Void where A: AnyObject
can be composed the same way. Writemver
,mut
, and^
in terms ofAnyObject
. Note that there is a specific subclass ofWritableKeyPath
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.
We’ve explored
<>
/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 forprop(\UIEdgeInsets.top) <> prop(\.bottom)
as a way of assigning bothtop
andbottom
the same value at once.Define an operator-free version of setters using
with
andconcat
from our episode on composition without operators. Define anupdate
function that combines the semantics ofwith
and the variadic convenience ofconcat
for ergonomics.In the Haskell Lens library,
over
andset
are defined as infix operators%~
and.~
. Define these operators and explore what their precedence should be, updating some of our examples to use them. Do these operators tick the boxes?
References
Swift Overture
Brandon Williams & Stephen Celis • Monday Apr 9, 2018We open sourced the Overture library to give everyone access to functional compositions, even if you can’t bring operators into your codebase.
Composable Setters
Stephen Celis • Saturday Sep 30, 2017Stephen spoke about functional setters at the Functional Swift Conference if you’re looking for more material on the topic to reinforce the ideas.
Semantic editor combinators
Conal Elliott • Monday Nov 24, 2008Conal 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
that 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!