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
Equatable protocol into an explicit datatype
Currently in Swift (as of 4.2) there is no way to extend tuples to conform to protocols. Tuples are what is
known as “non-nominal”, which means they behave differently from the types that you can define. For example, one
cannot make tuples
Equatable by implementing
extension (A, B): Equatable where A: Equatable, B: Equatable.
To get around this Swift implements overloads of
== for tuples, but they aren’t truly equatable, i.e.
you cannot pass a tuple of equatable values to a function wanting an equatable value.
However, protocol witnesses have no such problem! Demonstrate this by implementing the function
pair: (Combining<A>, Combining<B>) -> Combining<(A, B)>. This
function allows you to construct a combining witness for a tuple given two combining witnesses for each
component of the tuple.
Functions in Swift are also “non-nominal” types, which means you cannot extend them to conform to protocols.
However, again, protocol witnesses have no such problem! Demonstrate this by implementing the function
pointwise: (Combining<B>) -> Combining<(A) -> B>. This allows you to construct
a combining witness for a function given a combining witnesss for the type you are mapping into. There
is exactly one way to implement this function.
One of Swift’s most requested features was “conditional conformance”, which is what allows you to express,
for example, the idea that an array of equatable values should be equatable. In Swift it is written
extension Array: Equatable where Element: Equatable. It took Swift nearly 4 years after its launch
to provide this capability!
So, then it may come as a surprise to you to know that “conditional conformance” was supported for
protocol witnesses since the very first day Swift launched! All you need is generics. Demonstrate this by
implementing a function
array: (Combining<A>) -> Combining<[A]>. This is saying that
conditional conformance in Swift is nothing more than a function between protocol witnesses.
Currently all of our witness values are just floating around in Swift, which may make some feel
uncomfortable. There’s a very easy solution: implement witness values as static computed variables
on the datatype! Try this by moving a few of the witnesses from the episode to be static variables. Also try
array functions to be static functions on the
Protocols in Swift can have “associated types”, which are types specified in the body of a protocol but aren’t determined until a type conforms to the protocol. How does this translate to an explicit datatype to represent the protocol?
RawRepresentable protocol into an explicit datatype
struct RawRepresenting. You will
need to use the previous exercise to do this.
Protocols can inherit from other protocols, for example the
Comparable protocol inherits from the
Equatable protocol. How does this translate to an explicit datatype
to represent the protocol?
Comparable protocol into an explicit datatype
struct Comparing. You will need to use the
previous exercise to do this.
We can combine the best of both worlds by using witnesses and having our default protocol, too. Define a
DefaultDescribable protocol which provides a static member that returns a default witness of
Describing<Self>. Using this protocol, define an overload of
print(tag:) that doesn’t require a witness.
An old article detailing many of the pitfalls of Swift protocols, and how often you can simplify your code by just using concrete datatypes and values. Chris walks the reader through an example of some networking API library code, and shows how abstracting the library with protocols does not give us any tangible benefits, but does increase the complexity of the code.
Matt gives another account of protocol-oriented programming gone awry, this time by breaking down the famous WWDC talk where a shape library is designed using protocols. By rewriting the library without protocols Matt ends up with something that can be tested without mocks, can be inspected at runtime, and is more flexible in general.
Haskell’s notion of protocols are called “type classes,” and the designers of Swift have often stated that Swift’s protocols took a lot of inspiration from Haskell. This means that Haskellers run into a lot of the same problems we do when writing abstractions with type classes. In this article Gabriel Gonzalez lays down the case for scrapping type classes and just using simple datatypes.