A video series exploring functional programming and Swift.
#35 • Monday Oct 29, 2018 • Subscriber-only

Advanced Protocol Witnesses: Part 1

Now that we know it’s possible to replace protocols with concrete datatypes, and now that we’ve seen how that opens up new ways to compose things that were previously hidden from us, let’s go a little deeper. We will show how to improve the ergonomics of writing Swift in this way, and show what Swift’s powerful conditional conformance feature is represented by just plain functions.

This episode builds on concepts introduced previously:

#35 • Monday Oct 29, 2018 • Subscriber-only

Advanced Protocol Witnesses: Part 1

Now that we know it’s possible to replace protocols with concrete datatypes, and now that we’ve seen how that opens up new ways to compose things that were previously hidden from us, let’s go a little deeper. We will show how to improve the ergonomics of writing Swift in this way, and show what Swift’s powerful conditional conformance feature is represented by just plain functions.

This episode builds on concepts introduced previously:


Subscribe to Point‑Free

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

Introduction

In the last two weeks (episodes #33 and #34) we explored the idea that protocols could largely be translated into concrete datatypes, and in doing so we fixed some of the problems that protocols have, but more importantly, we opened up the doors to some amazing compositions that were completely hidden from us with protocols. Towards the end of those two episodes we claimed that knowing this correspondence between protocols and concrete datatypes could actually lead us to better API and library design, but stopped short on the details.

Well, we want to describe that, but before we can get there we need to go a little deeper with this correspondence. Currently our dictionary to translate between the world of protocols and the world of concrete types is woefully incomplete. There are tons of protocol concepts that we haven’t yet described how to translate over to concrete types, and knowing that information will provide us the tools we need to tackle API and library design. So, that is this week’s goal!

But first, we want to introduce a lil clean up terminology that we’ve been using on the series…

Subscribe to Point-Free

👋 Hey there! Does this episode sound interesting? Well, then you may want to subscribe so that you get access to this episodes and more!


Exercises

  1. 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.

  2. 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.

  3. 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?

  4. Translate the RawRepresentable protocol into an explicit datatype struct RawRepresenting. You will need to use the previous exercise to do this.

  5. 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?

  6. Translate the Comparable protocol into an explicit datatype struct Comparing. You will need to use the previous exercise to do this.


References

  • Protocol-Oriented Programming in Swift

    Apple • Tuesday Jun 16, 2015

    Apple’s eponymous WWDC talk on protocol-oriented programming:

    At the heart of Swift’s design are two incredibly powerful ideas: protocol-oriented programming and first class value semantics. Each of these concepts benefit predictability, performance, and productivity, but together they can change the way we think about programming. Find out how you can apply these ideas to improve the code you write.

  • Modern Swift API Design

    Apple • Wednesday Jan 2, 2019

    As of WWDC 2019, Apple no longer recommends that we “start with a protocol” when designing our APIs. A more balanced approach is discussed instead, including trying out concrete data types. Fast forward to 12:58 for the discussion.

    Every programming language has a set of conventions that people come to expect. Learn about the patterns that are common to Swift API design, with examples from new APIs like SwiftUI, Combine, and RealityKit. Whether you’re developing an app as part of a team, or you’re publishing a library for others to use, find out how to use new features of Swift to ensure clarity and correct use of your APIs.

  • Pullback

    We use the term pullback for the strange, unintuitive backwards composition that seems to show up often in programming. The term comes from a very precise concept in mathematics. Here is the Wikipedia entry:

    In mathematics, a pullback is either of two different, but related processes: precomposition and fibre-product. Its “dual” is a pushforward.

  • Protocols with Associated Types

    Alexis Gallagher • Tuesday Dec 15, 2015

    This talk by Alexis Gallagher shows why protocols with associated types are so complicated, and tries to understand why Swift chose to go with that design instead of other alternatives.

  • Protocol Oriented Programming is Not a Silver Bullet

    Chris Eidhof • Thursday Nov 24, 2016

    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.

  • Value-Oriented Programming

    Matt Diephouse • Sunday Jul 29, 2018

    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.

  • Scrap your type classes

    Gabriel Gonzalez • Wednesday May 2, 2012

    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.

  • Haskell Antipattern: Existential Typeclass

    Luke Palmer • Sunday Jan 24, 2010

    A Haskell article that demonstrates a pattern in the Haskell community, and why it might be an anti-pattern. In a nutshell, the pattern is for libraries to express their functionality with typeclasses (i.e. protocols) and provide Any* wrappers around the protocol for when you do not want to refer to a particular instance of that protocol. The alternative is to replace the typeclass with a simple concrete data type. Sound familiar?

  • Some news about contramap

    Brandon Williams • Monday Oct 29, 2018

    A few months after releasing our episode on Contravariance we decided to rename this fundamental operation. The new name is more friendly, has a long history in mathematics, and provides some nice intuitions when dealing with such a counterintuitive idea.

  • Contravariance

    Julie Moronuki & Chris Martin

    This article describes the ideas of contravariance using the Haskell language. In many ways exploring functional programming concepts in Haskell is “easier” because the syntax is sparse and allows you to focus on just the core ideas.

  • Protocol Witnesses: App Builders 2019

    Brandon Williams • Friday May 3, 2019

    Brandon gave a talk about “protocol witnesses” at the 2019 App Builders conference. The basics of scraping protocols is covered as well as some interesting examples of where this technique really shines when applied to snapshot testing and animations.

    Protocol-oriented programming is strongly recommended in the Swift community, and Apple has given a lot of guidance on how to use it in your everyday code. However, there has not been a lot of attention on when it is not appropriate, and what to do in that case. We will explore this idea, and show that there is a completely straightforward and mechanical way to translate any protocol into a concrete datatype. Once you do this you can still write your code much like you would with protocols, but all of the complexity inherit in protocols go away. Even more amazing, a new type of composition appears that is difficult to see when dealing with only protocols. We will also demo a real life, open source library that was originally written in the protocol-oriented way, but after running into many problems with the protocols, it was rewritten entirely in this witness-oriented way. The outcome was really surprising, and really powerful.

Chapters
Introduction
00:05
Renaming contramap
01:25
Witness ergonomics
09:52
Conditional conformance
19:25
Nested conditional conformance
30:54
Till next time…
34:53