Algebraic Data Types

Course 1 · Ch 6
Algebraic Data Types
Maybe and Either aren't magic — they're ordinary data declarations, and the real ancestor of rust1-6's Option and Result

This is one of the two chapters (alongside Chapter 8) where this course's own "origin point" framing pays off most directly and concretely. What rust1-6 introduced as Option<T> and Result<T, E> — genuinely modeled on exactly what's covered here.

data Declarations — Product Types

data Point = Point Int Int -- a "product" type — bundles two values together origin = Point 0 0

Point bundles two Ints together — a product type, named because the number of distinct Point values is the product of each field's own possible values. Structurally comparable to csharp2-5's own positional records, decades earlier.

Sum Types — Multiple Shapes, One Type

data Shape = Circle Double | Rectangle Double Double -- a "sum" type — EITHER shape, never both

This is the real deal: a value of type Shape is either a Circle or a Rectangle, never a hybrid of both — a sum type, named because the total possibilities are the sum of each variant's own. This is the direct genetic ancestor of rust1-6's own data-carrying enums.

The Real Lineage — Maybe and Either ARE Just ADTs

-- from the standard library — not special, not magic, just ordinary data: data Maybe a = Nothing | Just a data Either a b = Left a | Right b

Here's the central reveal: Maybe and Either aren't built-in language magic — they're defined using the exact same data syntax available to any programmer, sitting in the standard library like any other type. rust1-6's own Option<T> and Result<T, E> were explicitly modeled on these two — None/Some maps directly onto Nothing/Just, and Err/Ok maps directly onto Left/Right. Same shape, same idea, arriving in Haskell decades earlier.

Recursive Data Types

data Tree a = Leaf | Node (Tree a) a (Tree a) -- a real binary tree, in one line

A data type can reference itself — a genuine binary tree needs no more than this. Recursive data alongside haskell1-4's own recursive functions, and the two ideas reinforce each other constantly: processing a Tree almost always means writing a recursive function over it.

Record Syntax for Product Types

data Person = Person { name :: String, age :: Int } alice = Person { name = "Alice", age = 30 } name alice -- "Alice" — a real accessor function, generated automatically

Named-field syntax, comparable again to csharp2-5's own record syntax — Haskell's own version predates C#'s by decades too, though honestly, it carries a real historical wart C#'s doesn't share, covered next.

ConceptRust (rust1-6)C# (csharp2-5)Haskell
Product typestructrecord (positional)data with one constructor
Sum typeenum with datano direct built-in equivalentdata with | alternatives
"Optional value" typeOption<T> — modeled on Maybenullable reference types (csharp2-6)Maybe a — the real origin
"Success or failure" typeResult<T, E> — modeled on Eitherexceptions (csharp1-7)Either a b — the real origin
Reach for a sum type whenever a value is genuinely one of several distinct shapes
The same instinct rust1-6's own enum design encourages applies directly here — model "this OR that, never both" as a real sum type rather than a product type with unused/optional fields simulating the same idea.
Classic record syntax has a real field-naming limitation
Two different data types in the same module genuinely cannot both have a field called name without a real conflict — unlike csharp2-5's own records or Java's classes, where every field is automatically scoped to its own type with no possibility of collision. This is a well-documented, honest wart in Haskell's classic record syntax, not a made-up gotcha.

Coding Challenges

Challenge 1

Define a sum type TrafficLight with three constructors (Red, Yellow, Green, no arguments needed), and a function next :: TrafficLight -> TrafficLight that cycles Red -> Green -> Yellow -> Red.

📄 View solution
Challenge 2

Write your own version of Maybe called MyMaybe (data MyMaybe a = MyNothing | MyJust a) and a function safeDivide :: Int -> Int -> MyMaybe Int that returns MyNothing on division by zero and MyJust result otherwise.

📄 View solution
Challenge 3

Define a recursive Tree a data type and write a function treeSum :: Tree Int -> Int that recursively sums every value in the tree, testing it on a small tree with at least 4 nodes.

📄 View solution

Chapter 6 Quick Reference

  • Product types (data Point = Point Int Int) bundle multiple values — comparable to csharp2-5's positional records
  • Sum types (data Shape = Circle Double | Rectangle Double Double) are the real ancestor of rust1-6's own data-carrying enums
  • Maybe and Either are ordinary standard-library data types, not language magic — Nothing/Just and Left/Right are the real origin of Rust's None/Some and Err/Ok
  • A data type can reference itself — real recursive data (Tree a), reinforcing haskell1-4's own recursive functions
  • Classic record syntax has a genuine field-naming collision limitation across types in the same module — a real wart C#'s own records don't share
  • Next chapter: pattern matching — the real origin of the style rust1-6's match and csharp2-5's own pattern matching both descend from