Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Stephen: About 2 months ago we did quite a deep dive into macros. We showed how they work as a compiler plugin, we showed how to debug them, how to write deep and nuanced tests of them, including their diagnostics and fix-its, and we even played around with SwiftSyntax a bit to improve some open source macros from Apple and the community.
We are now ready to start creating a new macro from scratch that will help improve one of our most popular libraries, and we are going to use all of the knowledge we built up from those past episodes.
Brandon: There’s a tool that we built nearly 4 years ago that will greatly benefit from macros, and that’s “case paths.” Case paths are the dual concept to key paths in Swift that we all know and love. Key paths give you an abstract representation of the getter and setter for a mutable property on a type that you can reference without having an actual value at hand. You can pass this representation around in your program, and it is an important concept used in various algorithms in the Swift ecosystem, and is used a ton in SwiftUI. Honestly, key paths are one of the most powerful features in the Swift language, and there is no other programming language out there today, to our knowledge, that has first class support for such a concept.
Stephen: So, key paths are amazing, but sadly they do not work on enums. Structs and enums are like two sides of the same coin. It doesn’t make sense to talk about one without thinking about the other, and we’ve spent many episodes in the past trying to convince everyone of this fundamental fact. But unfortunately Swift often provides a lot of tools and affordances to structs without providing the corresponding tool for enums. And key paths are one such example.
Brandon: That is why we defined the concept of a “case path” in episodes nearly 4 years ago. They allow you to abstractly represent the two most fundamental operations you can do with a case of an enum: you can try to extract the payload from an enum value or you can embed a payload value into an enum. So key paths have getters and setters whereas case paths have extractors and embedders. And just as there are a lot of algorithms that make use of key paths, there are also a lot of algorithms that can make use of case paths. We have shown how powerful they can be in the Composable Architecture, but they are also useful in vanilla SwiftUI, and in parser-printers, and a whole bunch more places.
Stephen: We’ve tried over the years to drum up interest for first-class support of case paths in the Swift language, but nothing has really taken hold. So, it’s time to see how far we can get with macros to make this concept seem as native as possible in the Swift language. It still won’t be as good as if Swift supported it natively, but we can get a lot closer than what was possible before macros.
Let’s start with a quick introduction to case paths so that we all know what we are talking about here.