Lambda calculus encodings of algebraic data types
Disclaimer: Parts of this page have been generated with supervised LLM-AI assistance.
Encodings not yet checked for correctness. Please take them with a grain of salt.
Overview
Algebraic Data Types (ADTs) can be encoded in untyped lambda calculus in several different ways.
This article documents the main approaches and their trade-offs.
Core Encodings
ADTs are built from two fundamental building blocks:
- Product types (tuples/records)
- Sum types (variants/enums)
Each can be encoded in several ways:
Church Encoding
The classical direct approach:
Products:
pair = λx.λy.λf.(f x y) fst = λp.(p λx.λy.x) snd = λp.(p λx.λy.y)
Sums:
inl = λx.λl.λr.(l x) inr = λx.λl.λr.(r x) case = λs.λl.λr.(s l r)
Properties:
- Eager evaluation
- Direct construction and elimination
- Most straightforward implementation
Lazy Encoding
Delays evaluation through thunks:
Products:
pair = λx.λy.λf.(f λk.x λk.y) fst = λp.(p λx.λy.(x λw.w)) snd = λp.(p λx.λy.(y λw.w))
Sums:
inl = λx.λl.λr.(l λk.x) inr = λx.λl.λr.(r λk.x) case = λs.λl.λr.(s (λx.(x λw.w) l) (λx.(x λw.w) r))
Properties:
- Components only evaluated when needed
- Useful for infinite structures
- Higher memory usage due to closure allocation
Strict Encoding
Forces evaluation through wrapper functions:
Products:
pair = λx.λy.λf.(f (λk.k x) (λk.k y)) fst = λp.(p λx.λy.(x λw.w)) snd = λp.(p λx.λy.(y λw.w))
Sums:
inl = λx.λl.λr.(l x) inr = λx.λl.λr.(r x) case = λs.λl.λr.(s (λx.l x) (λx.r x))
Properties:
- Guarantees evaluation order
- Useful for maintaining invariants
- Additional function call overhead
Elimination-Focused (Continuation) Encoding
Represents data through its elimination behavior:
Products:
pair = λx.λy.λk.(k (λf.f x y)) fst = λp.(p λq.(q λx.λy.x)) snd = λp.(p λq.(q λx.λy.y))
Sums:
inl = λx.λk.(k (λl.λr.(l x))) inr = λx.λk.(k (λl.λr.(r x))) case = λs.λl.λr.(s λc.(c l r))
Properties:
- Centered on pattern matching
- Natural fit for programming language implementation because:
- Pattern matching is fundamentally about elimination
- Aligns with how most programming languages handle data inspection
- Compiler optimizations often work on elimination forms
- More complex implementation
Construction-Focused Encoding
Represents data through its construction behavior:
Products:
pair = λx.λy.(λk.k (λc.c x y)) fst = λp.(p (λq.q λx.λy.x)) snd = λp.(p (λq.q λx.λy.y))
Sums:
inl = λx.(λk.k (λl.λr.l x)) inr = λx.(λk.k (λl.λr.r x)) case = λs.λl.λr.(s (λc.c l r))
Properties:
- Dual to elimination-focused encoding
- Emphasizes data construction paths
- Less common in practice
Composition Patterns
Nesting Structure
ADTs typically use:
- Disjunctive Normal Form (DNF): Sum of Products
- Conjunctive Normal Form (CNF): Product of Sums
DNF is preferred in practice because:
- Maps naturally to constructors with fields
- More efficient pattern matching
- Better cache locality for related fields
- Matches programmer intuition
Example in DNF:
data Tree = Leaf Int
| Node Tree Int Tree
Encoding Consistency
Best practices for combining encodings:
- Use the same encoding style throughout (Church with Church, etc.)
- Stick to one nesting structure (typically DNF)
- Consider using elimination-focused style for programming language implementations
Implementation Considerations
Performance Characteristics
- Church encoding: Simple but eager
- Lazy encoding: Memory overhead but supports infinite structures
- Strict encoding: Predictable evaluation but extra calls
- Elimination-focused: Complex but good for pattern matching
- Construction-focused: Similar complexity to elimination-focused
Language Implementation
Most programming languages use:
- DNF structure
- Elimination-focused approach
- Optimized representations at lower levels
Type System Implications
- All encodings work in untyped lambda calculus
- Simply-typed lambda calculus cannot encode proper product types of different types
- Sum types are encodable in simply-typed lambda calculus
Advanced Topics
Related to the afire mentioned limit for product types.
There may interesting connections of these encodings to things like GADTs and dependent types.
The relationship between continuation-based encodings and categorical semantics (particularly through the continuation monad)
suggests deeper connections to Cartesian closed categories and type theory.
Notice the duality:
– Corecursive construction of ADTs via type constructors
– Recursive deconstructions via pattern matching in case … of … statements
The case … of … statements are where branching happens which is what makes control flow rich and interesting.
Related
External links
- Simply Typed Lambda Calculus
- Continuation-passing style
- Pattern matching (programming)
- Algebraic data type