On 21 March 2019 I gave a lighning talk about Refinement Types at NSBarcelona‘s Universal links, Deep Linking and Routing @ Inqbarna HQ event.
I started the talk with a quote from Tony Horare:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
And then, another one from Robert C. Martin (Uncle Bob):
The question is: Whose job is it to manage the nulls. The language? Or the programmer?
These languages [Swift and Kotlin] are like the little Dutch boy sticking his fingers in the dike. Every time there’s a new kind of bug, we add a language feature to prevent that kind of bug. And so these languages accumulate more and more fingers in holes in dikes. The problem is, eventually you run out of fingers and toes.
After those contrast views on how to develop software, I explained an experiment I was conducting on how to implement Refinement Types in Swift.
If you feel curious, you can check the slides here.
Type transparency
At the moment of preparing the slides, I was experimenting on making refined types and their base types interchangeable.
In the past, I had developed swift-sourcerer, a proof-of-concept metaprogramming framework based on Sourcery. And I thought I could use it along with GYB to generate the boilerplate code required to achieve type transparency for refinement types.
As a result, I created 2 repositories, Wrapper and Features, that could be used to make refined types more interchangeable with their base Swift types.
Fluent DSL
Lately, however, I have been more focused on defining a DSL to work in a more convenient way with refinement types.
Initially I was using simple typealias
to make code more readable (e.g. String.NonEmpty
instead of Refined<Not<Empty<String>>>
).
It was an improvement, but it fell short in terms of combination of predicates (String.CountMoreThanTwo
, String.CountLessThanThree
, etc.).
For this reason I decided to use a GYB template to autogenerate more complex typealias
that allow to combine different predicates in a fluent interface fashion.
See some examples:
String.Count<Int_Four>.Or.Empty("") // is valid
String.Count<Int_Four>.Or.Empty("1") // is invalid
String.Count<Int_Four>.Or.Empty("12") // is invalid
String.Count<Int_Four>.Or.Empty("123") // is invalid
String.Count<Int_Four>.Or.Empty("1234") // is valid
Character.Letter.And.Lowercase("a") // is valid
Character.Letter.And.Lowercase("0") // is invalid
Character.Letter.And.Lowercase("A") // is invalid
Or clone the repo and try it out yourself. 😉