-
Notifications
You must be signed in to change notification settings - Fork 4
0008: Email Tutorials Haskell For Beginners ‐ Basics of Sum Types in Haskell
- 8.1. From Product Types to Sum Types
- 8.2. Sum Types and Union Types: The Big Idea
- 8.3.
Boolas the Simplest Sum Type - 8.4. Understanding the
BoolDefinition in Haskell - 8.5. Defining Your Own Sum Type:
Month - 8.6. Enumeration Types and Nullary Constructors
- 8.7. Why Sum Types Are Better Than Using
Int - 8.8. Glossary of Terms for This Lesson
Earlier, you learned about product types, which combine several values together into a single structure, such as a custom Date type built from multiple components. In this lesson, you meet another fundamental concept in Haskell’s type system: sum types. While product types correspond to “and” combinations of values (this and that), sum types capture “or” relationships (this or that). Together, product and sum types give you powerful tools to model real-world concepts precisely in your code.
Sum types in Haskell are sometimes called union types, because they are conceptually similar to unions in set theory. Instead of saying “a value contains these components,” you say “a value can be one of these possibilities.” Technically, Haskell uses tagged union types, which means each possible form is clearly labeled so the compiler always knows which alternative you have. As a beginner, you do not need to worry about the detailed distinction between tagged and untagged unions. In this context, you can treat the terms sum type and union type as interchangeable.
You have already used the Bool type in Haskell. When you declare a variable with type Bool, it can only take one of two values: True or False. There is no such thing as “half-true” or “partly false”; the type itself strictly limits the possible values. This is an important characteristic of sum types: the type enumerates exactly which values are valid. In the case of Bool, those values are True and False—nothing else.
To see how sum types are written, the lesson shows the actual definition of Bool in Haskell. Conceptually, it looks like this:
data Bool = True | FalseLet’s break this down:
-
dataintroduces a new type definition. -
Boolis the name of the type. -
=begins the list of constructors for this type. -
TrueandFalseare the constructors, separated by a pipe symbol (|).
You can read the pipe symbol as “or,” so the whole definition reads naturally as:
“We are defining a data type called
Bool, which has the constructorsTrueorFalse.”
An important rule is that the type name (Bool) and all constructor names (True, False) start with a capital letter. The pipe (|) is the key piece of syntax for sum types: it separates the different alternatives that a value of the type may take. Bool is a sum type because the number of possible values is the sum of its constructors—in this case, two.
To solidify the idea, the lesson gives a small exercise: define a sum type Month that lists all months of the year, and then create some values of that type. The definition follows the same pattern as Bool:
data Month
= January
| February
| March
| ...
deriving ShowHere:
-
Monthis the type name. -
January,February,March, and so on are the constructors, each representing one month. - The constructors are separated by the
|symbol, meaning “January or February or March …”. -
deriving Showis added so that values of typeMonthcan be displayed in GHCi without errors.
To create values, you give them type Month and use the appropriate constructor:
jan :: Month
jan = January
feb :: Month
feb = FebruaryThese constructors do not take arguments; you simply choose which one you want when defining a value.
The sum types shown so far—Bool and Month—are very simple:
- Their constructors take no arguments.
- Each constructor stands alone as one possible value.
Constructors that take no arguments are called nullary constructors. Data types like Bool and Month, whose constructors simply list all possible values in a fixed set, are often referred to as enumeration types (or enums). They “enumerate” every valid option for the concept you are modeling. For months, that list is finite and fixed, which fits enumeration perfectly.
You could try to represent months using an Int and agree informally that 1 means January, 2 means February, and so on. However, this is error-prone and less clear:
- An
Intcan represent far more values than there are months (like 0, 13, 1000), which have no meaning as months. - A function taking an
Intdoes not communicate that it expects a month; its intent is hidden.
By defining a custom sum type like Month, you achieve:
-
Clarity: type signatures involving
Monthclearly state that the values represent months. -
Safety: you cannot accidentally pass
42as a month, because the type system restricts you to constructors such asJanuary,February, etc.
In other words, sum types let you encode your domain logic directly into the type system, making invalid states harder (or impossible) to represent.
-
Sum type A type that can take one of several alternative forms, each represented by a constructor. The total number of possible values is the sum of the possibilities contributed by each constructor.
-
Union type Another name for a sum type, inspired by set unions. In Haskell, unions are tagged so the compiler always knows which alternative a value belongs to.
-
Tagged union A union type where each alternative has an associated tag (constructor name), making the alternative explicit and safe to inspect.
-
Constructor (data constructor) A named option in a sum type definition that creates values of that type. For
Bool,TrueandFalseare data constructors. -
Pipe symbol (
|) The symbol used in Haskell type declarations to separate alternative constructors. It can be read as “or,” as inTrue | False. -
Nullary constructor A constructor that takes no arguments. Examples include
True,False,January,February, etc. -
Enumeration type (enum) A sum type whose constructors simply list all possible values in a fixed, finite set, such as all months of the year or all days of the week.
-
BoolA built-in sum type with exactly two values:TrueorFalse. It is one of the simplest and most commonly used sum types. -
Month(custom example) A user-defined sum type that enumerates all months. It demonstrates how to model a fixed set of meaningful values instead of using raw integers. -
deriving ShowA clause added to type declarations that instructs the compiler to automatically generate code for converting values of that type into strings, so they can be printed in GHCi.
Bernard Sibanda is a global Technology Entrepreneur, Web3 and Software Consultant with a deep focus on Cardano Blockchain, Midnight and Community building.
Key Positions:
- Founder, CTO, Developer Advocate cohort #1, Fullstake Developer, Cardano Ambassador, Catalyst Project Manager, DREP-WIMS:
- Co-founder of ABL Tech and Cardano Africa Live
- EBU-certified Plutus Pioneer (Plutus/Haskell)
- Cohort #1 Plutus Pioneer Developer
- Catalyst Community Reviewer & Funded Projects Manager
-
DRep for WIMS-Cardano (ID:
drep1yguj8zu48n99pv70yl6ckzt9hdgjy8yjnlqs2uyzcpafnjgu4vkul) - Intersect Developer Advocate
- Intersect Committe Member 2025-2026
- Cardano Marketer,Promoter and blogger
- Cardano Open Source Contributor
- Cardano communities and events organizer and builder
- Cardano Ambassador for South Africa
Official links:
- Stablecoins Dex
- Coxygen Global Universities
- WIMS Cardano Global
- Cardano Africa Live
- WIMS Cardano Videos
- Cardano Smart Contract Videos
- Fullstack IT Consulting
Social links: