← back to foundations

Types and Functions

Bartosz Milewski · 2014 · Category Theory for Programmers, Ch. 2

Prereqs: 🍞 Chapter 1 (Category). 5 min.

Types are wpsets. Functions are morphisms between them. The category Hask has Haskell types as objects and pure functions as arrows. Every type is extended with bottom (⊥), the value representing non-termination.

Void |Void| = 0 () Unit |Unit| = 1 T F Bool |Bool| = 2 absurd unit const T const F |A → B| = |B|^|A| functions from Unit pick elements; functions to Unit are unique

Types as sets

A type is a set of values. Bool is the two-element set {True, False}. Integer is an infinite set. A function between types is a mapping between sets. This is the category Set: objects are sets, morphisms are functions. Programming types live in the same structure.

Scheme

Pure functions

A pure function always produces the same output for the same input and has no side effects. Pure functions are the only kind that qualify as morphisms in a category. Impure functions (I/O, mutation, randomness) break composability: if f's output depends on hidden state, g ∘ f is not determined by the types alone.

Scheme

Bottom — the value that never arrives

Every Haskell type secretly includes one extra value: bottom (⊥), representing non-termination. A function that loops forever or throws an exception returns bottom. This makes Hask different from Set: in Set, every function terminates. In Hask, a function Bool → Bool can return True, False, or ⊥. Functions that may return bottom are partial; those that always return a proper value are total.

Scheme

Void — the empty set

The type Void has no values at all. It corresponds to the empty set. You cannot call a function that takes Void as input, because you have nothing to pass it. But you can declare one: absurd :: Void → a. From falsity, anything follows. This is the wpCurry-Howard correspondence in action.

Scheme

Unit — the singleton set

The type () (unit) has exactly one value. Functions from unit to a type A are in one-to-one correspondence with elements of A: each such function "picks" one value. The function unit :: a → () discards its argument and returns the single value. It is the unique morphism to the terminal object.

Scheme

Bool — the two-element set

Bool has exactly two values. Functions to Bool are predicates: they classify inputs into true/false. Functions from Bool select a pair of values from the target type. Bool is the simplest interesting type: big enough to branch on, small enough to enumerate.

Scheme

Notation reference

Haskell Scheme Python Meaning
Void; (no values)NoReturnEmpty type (initial object)
()'unitNoneUnit type (terminal object)
Bool#t / #fTrue / FalseTwo-element set
f :: a -> b(define (f x) ...)def f(x): ...Morphism from a to b
⊥ (bottom)(let loop () (loop))while True: passNon-termination
absurd :: Void -> a(define (absurd v) ...)def absurd(v): ...From falsity, anything
unit :: a -> ()(define (to-unit x) 'unit)def to_unit(x): NoneTerminal morphism
Neighbors

Milewski chapters

Related paper pages

Foundations (Wikipedia)

Translation notes

All examples use total functions in Scheme and Python. The bottom example simulates non-termination with an infinite loop but does not actually run it. Milewski's treatment works in Hask, where every type includes bottom and the category is technically a CPO-enriched category rather than Set. The examples on this page stay in Set (no bottom, no laziness). For example: the four Bool→Bool functions enumerated above are exactly the four morphisms in Set. In Hask, there are additional "functions" that return bottom on some inputs, bringing the count higher. The enumeration is exact for Set. The Hask subtlety is noted but not demonstrated.

Ready for the real thing? Read Chapter 2. Start with "What Are Types?" for the sets-and-functions picture, then "Examples of Types" for Void, Unit, and Bool.

Framework connection: Types are the handshake between pipeline stages. The target type of one morphism must match the source type of the next. This is the composability constraint that the Natural Framework enforces at every stage boundary. (jkThe Handshake)