Chapter 7


Tips ⚠️️

  • Use try.purescript.org to test out the code examples in this book.
  • Whenever the example code starts with module Main where, make sure to clear out the code editor on try.purescript.org before pasting new code in. This will help to avoid unncessary errors

Sum Types

At the beginning of this book, we talked about the two big categories of tools we have at our disposal when writing programs: input and output. When we write code for a program, we're working with a very primitive yet powerful form of input. We create variables to store our data, yet the type of data we can store is restricted to text, numbers, and symbols. We've worked with text via the String type and numbers via the Int and Number types, but now we move on by taking a look at symbols. I want to apologize now because what I've been calling symbols is more formally known as sum types (also ADTs, short for Algebraic Data Types).

What are sum types?

Sum Types are a bit different from the other types we've worked with so far. Unlike String or Int, these types align more closely with real world concepts. For example, perhaps we want a variable that stores the current day of the week, or the grade you got on your last math test; with sum types, we can create these types easily. Let's try it out.

The "Days of the Week" type

module Main where
import Prelude

data DayOfTheWeek
  = Monday
  | Tuesday
  | Wednesday
  | Thursday
  | Friday
  | Saturday
  | Sunday

-- Create a variable to store the current day of the week
currentDay :: DayOfTheWeek
currentDay = Sunday

Alright! We got some new syntax to look at. Let's break this down a bit.

We create new types using the keyword data, followed by the name of our type.
Warning: Type names always start with a capital letter. Like variables and functions, spaces aren't allowed either.

Here's an example:

module Main where
import Prelude

data CoolType

data NotCoolType

This alone is valid syntax and creates the types CoolType and NotCoolType, but if we stop here, what we've created is our own version of Void types - types with no values. To add values to our types, we add an = after our type name followed by a list of values.

module Main where
import Prelude

data CoolType
  = Cool

data NotCoolType
  = UnCool

Both CoolType and NotCoolType now have one valid value. At this point we've moved from creating our own version of Void to creating our own version of Unit! In order to move on to more useful types, we need one more piece of syntax; the | character. The | character could be read as the word "or"; we use it to give our types more than one value. Let's use it to give our types a few more useful values.

module Main where
import Prelude

data CoolType
  = Cool
  | VeryCool
  | SuperCool

data NotCoolType
  = UnCool
  | VeryUnCool
  | AbsolutelyLame
  | UnthinkablyLame

Finally, our types have become much more interesting now. Our type CoolType has three valid values to choose from, and NotCoolType has four. With that explanation behind us, we should be able to understand our original DayOfTheWeek example now:

module Main where
import Prelude

data DayOfTheWeek
  = Monday
  | Tuesday
  | Wednesday
  | Thursday
  | Friday
  | Saturday
  | Sunday

-- Create a variable to store the current day of the week
currentDay :: DayOfTheWeek
currentDay = Sunday

As demonstrated above, to create a variable using that type, we give it a type declaration using the types name. When we assign it a value, we use one of the values listed after the types = sign:

currentDay :: DayOfTheWeek -- Type declaration using the name we gave our type
currentDay = Wednesday -- Value from the list of values

bestDayEver :: DayOfTheWeek
bestDayEver = Friday

humpDay :: DayOfTheWeek
humpDay = Wednesday

Why go through the trouble?

We could use Strings or Ints to do this, but that would be making life hard on ourselves. For example, we could implement our DayOfTheWeek type using Strings like this:

module Main where
import Prelude

currentDay :: String
currentDay = "Sunday"

-- This doesn't make sense
tomorrow :: String
tomorrow = "PlutoDay"

-- What..?
yesterday :: String
yesterday = "Yo yo yo!"

The problem is, nothing is preventing us from entering values that have nothing to do with the days of the week. On the converse, if we use our official DayOfTheWeek type, the compiler protects us from such silly errors:

module Main where
import Prelude

data DayOfTheWeek
  = Monday
  | Tuesday
  | Wednesday
  | Thursday
  | Friday
  | Saturday
  | Sunday

-- Create a variable to store the current day of the week
currentDay :: DayOfTheWeek
currentDay = Sunday

-- This will cause an error: "Unknown type PlutoDay"
tomorrow :: DayOfTheWeek
tomorrow = PlutoDay

-- This will cause an error: "Can't match String with DayOfTheWeek
yesterday :: DayOfTheWeek
yesterday = "Yo yo yo!"

As you can see from the above examples, handling this manually using the primtive types would be taking on a lot of mental burden that the compiler would otherwise automatically take care of for us.

Summary

We can create our own types using the data keyword. The general syntax used to create types using data is as follows:

data YourTypesName
  = Value1
  | Value2

-- And create a variable to use them like this:
someVariable :: YourTypesName
someVariable = Value1

Types created in this way are called sum types. We can use these types to create variables that run closely with real world concepts, such as days of the week, school grades, etc.

We haven't seen everything sum types have to offer just yet, but we'll visit that at a later time.

Self Practice

1. Create a data type to represent school grades and then create a variable giving yourself the best grade you remember getting on a Math test. (Or any other test if you really don't like math).

2. Create a data type for your top five desserts, then create a variable that stores your favorite one. (Use spy to output your answer to the console for extra points!)


Answers

Question 1.

module Main where
import Prelude

data LetterGrade
  = A
  | B
  | C
  | D
  | F

lastMathGrade :: LetterGrade
lastMathGrade = C -- Don't judge, it was a hard class :'(

Question 2.

module Main where
import Prelude
import Debug.Trace

data BestDessert
  = Brownie
  | CinnamonMuffin
  | CinnamonDonut
  | CheeseCake
  | DanishPastry

favoriteDessert :: BestDessert
favoriteDessert = spy "My favorite dessert is" CinnamonMuffin -- The ones from costco man...