6.5

Type Classes

Haskell typeclass hierarchy. Monad, Applicative, Functor.

Haskell Type Classes — Brief ☧

"In whom are hid all the treasures of wisdom and knowledge."

— Colossians 2:3 (KJV)

Deep version → | Back to Category Theory →


Q: You know how a USB port works with many different devices — a

keyboard, a mouse, a flash drive — even though each device does

something completely different? They all conform to the same interface.

The port does not care about the specifics; it just knows the device

speaks USB. Is there a way to express this idea in a type system?

A: That is exactly what a type class is. A type class declares

an interface — a set of operations that any conforming type must provide.

Each type that "implements" the class supplies its own version of those

operations. Different types, one shared interface.

-- For God so loved the world that he gave his only begotten Son,
-- that whoever believes in him should not perish but have eternal life.
-- John 3:16

-- The interface: every gift can be exercised
class GiftChirho a where
    exerciseChirho :: a -> String

-- Prophecy: one type, its own implementation
data ProphecyChirho = ProphecyChirho String
instance GiftChirho ProphecyChirho where
    exerciseChirho (ProphecyChirho msg) = "Thus saith the Lord: " ++ msg

-- Healing: different type, same interface
data HealingChirho = HealingChirho String
instance GiftChirho HealingChirho where
    exerciseChirho (HealingChirho who) = who ++ " is healed!"

Q: That reminds me of interfaces in Java or protocols in Swift.

What makes type classes special?

A: Two things. First, you can add a type class instance after the

type is defined — you do not need to modify the original code. Second,

type classes form a hierarchy. Functor (the ability to map over

contents) is the simplest. Applicative builds on it (apply wrapped

functions). Monad extends that further (chain computations where

each step depends on the previous result). Each level adds capability,

like building blocks stacking on each other.

Q: Can you give me a concrete everyday example of this hierarchy?

A: Sure. Think about working with a list — an array

of values:

  • Functor: you can map a function over every element (double each number).
  • Applicative: you can apply a list of functions to a list of values (every combination).
  • Monad: each element produces a new list, and you flatten the results (like a search that branches at every step).

Each level is strictly more powerful, and each includes the abilities

of the levels below it.

The Core Hierarchy

Type ClassCore OperationPlain-English Analogy
Functorfmap — transform contentsApply one function to every item in a box
Applicative<*> — apply wrapped functionApply multiple boxed functions to boxed values
Monad>>= (bind) — chain computationsEach step's output decides the next step's input
Semigroup<> — combine two valuesMerge two linked lists end-to-end
Monoidmempty + <> — identity + combineAn empty list is the identity: merging with it changes nothing

The hierarchy in this table is one of the most important concepts in typed functional programming. Notice how each type class builds on the ones above it: Functor lets you transform contents, Applicative lets you combine multiple containers, and Monad lets you chain dependent computations. Semigroup and Monoid capture the idea of combining values, which is fundamental to everything from string concatenation to parallel reduction. Understanding this hierarchy gives you a vocabulary for describing computational patterns that recur across every domain of programming.

Connection to our system: Our semiring_chirho.rs implements the Semiring

type class with four different instances: Bool (for exact constraint solving), Probability (for soft inference), Tropical (for optimization), and Counting (for solution enumeration). Each semiring provides

its own plus_chirho (how alternatives combine) and times_chirho (how conjunctions combine), unified by one interface. The power of this design is that you write

the constraint propagation algorithm once, and the type class picks the right

implementation at compile time. When the FPGA runs in Boolean mode, times is AND and plus is OR. When the CPU runs in Probability mode for differentiable inference, times is multiplication and plus is probabilistic sum. Same algorithm, different interpretations -- exactly the kind of principled abstraction that type classes were designed for.

Learn more in the deep version

Related: Effect Systems | Category Theory | Lenses


Soli Deo Gloria

Self-Check 1/1

In Haskell, Monad extends: