Typeclasses, A First Look
Fundamentals closes with the chapter this whole course has been building toward: the mechanism behind every ==, every show, every print call used since Chapter 1 — and the real, documented origin of an idea already met on this site under a different name.
The Problem Typeclasses Solve — Ad-Hoc Polymorphism
== needs to behave differently for Int, for String, for a custom Shape — but should still be called the exact same way regardless. This is ad-hoc polymorphism: one function name, genuinely different implementations per type, dispatched based on which type is actually involved. A real, important contrast with haskell1-2's own parametric polymorphism (id :: a -> a), which works identically for every type with zero per-type customization at all.
class and instance — Defining a Typeclass
A class declares a capability; an instance provides the actual per-type implementation. Any type can gain MyEq by writing its own instance — the capability isn't limited to types the language ships with.
Eq, Ord, and Show — The Standard Trio
Eq gives ==//=; Ord gives </>/compare; Show gives the conversion to String that has been powering every print/show call used throughout this entire course, now properly explained. deriving, already used without comment in haskell1-6 and haskell1-7, is GHC automatically writing the obvious structural instance for straightforward cases.
The Real Ancestor of rust2-2's Traits
Here's the closing reveal: Haskell's typeclasses, introduced in 1988, are the direct historical ancestor of rust2-2's own trait system. Genuinely the same core idea — a capability defined separately from any specific type, implemented per-type, dispatched by which type is actually involved. Rust's own designers have been explicit about this lineage. Stated plainly, closing the loop this entire course opened back in haskell1-1: this course exists to show where these ideas really came from, and this is the clearest case of all.
How Dispatch Actually Works — Dictionary Passing, Briefly
A real, honest technical note, kept light: GHC implements typeclass polymorphism by secretly passing a "dictionary" of the relevant functions alongside any call using a typeclass-constrained function. This is genuinely different from java1-5's/csharp1-5's own vtable-based dynamic dispatch, and different again from cpp2-1's/rust2-3's monomorphization — a real, distinct third mechanism, worth naming even without going deep here.
| Aspect | Java interfaces (java1-6) | Rust traits (rust2-2) | Haskell typeclasses |
|---|---|---|---|
| Arrival | Java 1.0 (1996) | Rust 1.0 (2015) | 1988 — the real origin |
| Dispatch mechanism | vtable | monomorphization (usually) | dictionary passing |
| Can implement for types you don't own | no | yes, with restrictions | yes |
deriving (Eq, Ord, Show) covers the overwhelming majority of real cases — reach for a hand-written instance only when a type genuinely needs custom comparison or formatting logic beyond straightforward structural equality.
Eq a => a -> a -> Bool — the Eq a => part is a real, load-bearing part of the type signature. Omitting it while the function body still uses == produces a genuine compile error the moment GHC checks the body against the (now too-weak) declared signature — a good, early checkpoint that catches a real mistake immediately.
Coding Challenges
Define a data type Color = Red | Green | Blue with deriving (Eq, Show), and write an expression demonstrating both == comparing two Colors and show converting one to a String.
📄 View solutionDefine a typeclass Describable with a method describe :: a -> String, write instance declarations for two different types (e.g. Int and Bool), and call describe on a value of each.
📄 View solutionWrite a function that uses == inside its body but omit the Eq a => constraint from its type signature. Show the resulting compile error and explain why adding the constraint fixes it.
📄 View solutionChapter 8 Quick Reference — Course 1 Complete
- Ad-hoc polymorphism (one name, per-type implementations) is genuinely different from haskell1-2's own parametric polymorphism (identical for every type)
- class declares a capability; instance provides the per-type implementation, for any type, including ones you don't own
- Eq/Ord/Show are the standard trio — Show is what's been powering every print/show call since Chapter 1; deriving automates the straightforward case
- Typeclasses (1988) are the real, documented historical ancestor of rust2-2's own trait system, arriving decades earlier
- Dictionary passing is Haskell's own dispatch mechanism — a genuine third alternative to Java/C#'s vtables and C++/Rust's monomorphization
- A typeclass constraint (Eq a =>) is a required, load-bearing part of a signature — omitting it while using == is a real compile error
- Haskell Fundamentals is now complete. Course 2 (Intermediate/Advanced) begins with Functors — the first step toward the big monad reveal.