Algebraic Data Types (ADTs) are composite types in Haskell that allow you to create custom data structures.
See also: Pattern Matching and Recursion, Lists and List Comprehensions, Polymorphism, Type Synonyms, Newtype, Maybe, Either, Functors and Applicatives
Basic Syntax
ADTs are defined using the data keyword:
data TypeName = Constructor1 [Types] | Constructor2 [Types] | ...Types of ADTs
Sum Types (Enumerations)
Sum types provide alternatives, similar to enums in other languages:
data Season = Spring | Summer | Autumn | WinterHere, Season can be any one of the four constructors.
Product Types
Product types combine multiple values:
data Point = Point Float Float
-- ^ ^ ^
-- | | Second coordinate (Float)
-- | First coordinate (Float)
-- Constructor nameMixed Sum and Product Types
Many ADTs combine both concepts:
data Shape = Circle Float -- Radius
| Rectangle Float Float -- Width and Height
| Triangle Float Float Float -- Three sidesRecord Syntax
For product types with many fields, record syntax provides named fields:
data Person = Person { name :: String
, age :: Int
, email :: String
}Benefits of record syntax:
- Automatic accessor functions (
name,age,email) - Easier to create/update values with many fields
- Better readability for complex types
Recursive Data Types
See: Lists and List Comprehensions, Pattern Matching and Recursion
ADTs can be recursive, referring to themselves in their definition:
data List a = Empty | Cons a (List a)data Tree a = Leaf | Node a (Tree a) (Tree a)These recursive structures enable complex data structures like lists, trees, and graphs.
Parameterized Types
See: Polymorphism, Maybe, Either
Types can be parameterized with type variables:
data Maybe a = Nothing | Just adata Either a b = Left a | Right bThis enables generic programming, similar to generics in other languages.
Type Synonyms
Type synonyms create aliases for existing types:
type String = [Char]
type Name = String
type Age = Int
type Person = (Name, Age)Unlike data, type doesn’t create a new type; it just provides an alternative name.
Newtype
For single-constructor, single-field types, newtype provides a more efficient alternative to data:
newtype Age = Age IntBenefits of newtype:
- No runtime overhead
- Creates a distinct type (unlike
type) - Compiler guarantees it has exactly one constructor with one field
Pattern Matching with ADTs
See: Pattern Matching and Recursion
ADTs are typically used with pattern matching:
showSeason :: Season -> String
showSeason Spring = "It's spring!"
showSeason Summer = "It's summer!"
showSeason Autumn = "It's autumn!"
showSeason Winter = "It's winter!"area :: Shape -> Float
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Triangle a b c) = sqrt (s * (s - a) * (s - b) * (s - c))
where s = (a + b + c) / 2 -- Semi-perimeterThe Maybe Type
See: Error Handling, Common Monads
Maybe is a built-in ADT that represents optional values:
data Maybe a = Nothing | Just aIt’s used to handle potential failures without exceptions:
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)Deriving Typeclasses
See: Polymorphism, Functors and Applicatives
Common behaviors can be automatically derived:
data Season = Spring | Summer | Autumn | Winter
deriving (Show, Eq, Ord, Enum)This gives the type:
- String representation (
Show) - Equality testing (
Eq) - Comparison operations (
Ord) - Enumeration capabilities (
Enum)
Key Points to Remember
- ADTs are the primary way to create custom data types in Haskell
- They can represent both alternatives (sum types) and combinations (product types)
- Pattern matching is the primary mechanism for working with ADTs
- Type parameters and recursion make ADTs extremely flexible
MaybeandEitherare important built-in ADTs for handling potential failures