Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
Introduction
We often work with types that are far too general or hold far too many values than what is necessary in our domains. In our past episode on algebraic data types we used algebra to help simplify certain types to chisel them down to their core essentials. However, sometimes that isn’t possible. Sometimes we just want to differentiate between two seemingly equivalent values at the type level.
For example, an email address is nothing but a string, but it should be restricted in the ways in which it can be used. Also, ids in our code may all be integers, but it doesn’t make sense to compare a user’s id with, say, a subscription id. They are completely different entities.
We want to show that Swift, and in particular Swift 4.1, gives us an incredible tool to lift types up to a higher level so that they can distinguished from each other more easily. This helps improve the contracts we are building into our functions and methods, and overall makes our code safer.
Subscribe to Point-Free
Access this episode, plus all past and future episodes when you become a subscriber.
Already a subscriber? Log in
Exercises
Conditionally conform
Tagged
toExpressibleByStringLiteral
in order to restore the ergonomics of initializing ourUser
’semail
property. Note thatExpressibleByStringLiteral
requires a couple other prerequisite conformances.Conditionally conform
Tagged
toComparable
and sort users by theirid
in descending order.Let’s explore what happens when you have multiple fields in a struct that you want to strengthen at the type level. Add an
age
property toUser
that is tagged to wrap anInt
value. Ensure that it doesn’t collide withUser.Id
. (Consider how we taggedEmail
.)Conditionally conform
Tagged
toNumeric
and alias a tagged type toInt
representingCents
. Explore the ergonomics of using mathematical operators and literals to manipulate these values.Create a tagged type,
Light<A> = Tagged<A, Color>
, whereA
can represent whether the light is on or off. WriteturnOn
andturnOff
functions to toggle this state.Write a function,
changeColor
, that changes aLight
’s color when the light is on. This function should produce a compiler error when passed aLight
that is off.Create two tagged types with
Double
raw values to representCelsius
andFahrenheit
temperatures. Write functionscelsiusToFahrenheit
andfahrenheitToCelsius
that convert between these units.Create
Unvalidated
andValidated
tagged types so that you can create a function that takes anUnvalidated<User>
and returns anOptional<Validated<User>>
given a valid user. A valid user may be one with a non-emptyname
and anemail
that contains an@
.
References
Tagged
Brandon Williams & Stephen Celis • Monday Apr 16, 2018Tagged
is one of our open source projects for expressing a way to distinguish otherwise indistinguishable
types at compile time.
Tagged Seconds and Milliseconds
Brandon Williams • Wednesday Jul 18, 2018In this blog post we use the Tagged
type to provide a type safe way for
interacting with seconds and milliseconds values. We are able to prove to ourselves that we do not misuse
or mix up these values at compile time by using the tagged wrappers.
Type-Safe File Paths with Phantom Types
Brandon Kase, Chris Eidhof, Florian Kugler • Thursday Oct 5, 2017In this Swift Talk episode, Florian and special guest Brandon Kase show how to apply the ideas of phantom types to create a type safe API for dealing with file paths. We’ve used phantom types in our episode on Tagged to provide a compile-time mechanism for distinguishing otherwise indistinguishable types.