GHC Internals, History and Best Practices

53.1 How GHC Compiles Haskell

Inspect intermediate stages: -ddump-simpl (Core), -ddump-stg-final, -ddump-cmm, -ddump-asm. GHC also has JavaScript and WebAssembly backends (GHC 9.6+).

Source → Parser → Renamer → Type checker (with constraint solving)
       → Desugar to Core (System FC: a small typed lambda calculus)
       → Core-to-Core optimization (Simplifier: inlining, specialization, strictness analysis, fusion via RULES)
       → STG (Spineless Tagless G-machine) → Cmm (C--) → native code generator or LLVM → machine code
Runtime system (RTS): green threads (M:N scheduling), parallel generational GC (+ nonmoving GC option),
STM, async exceptions, profiling, eventlog

53.2 History

FPCA conference: committee formed to design a common lazy functional language GHC first release

Haskell 1.3: monadic IO, type constructor classes Haskell 98 standard

Monad transformers, STM (2005), GHC's growth Haskell 2010 standard

Applicative–Monad Proposal, Foldable/Traversable in the Prelude (GHC 7.10)

Semigroup–Monoid proposal (Semigroup a superclass of Monoid)

GHC 9.0 (linear types), Haskell Foundation founded, GHC2021 edition

OverloadedRecordDot, JS/WASM backends, GHC2024, HLS maturity, -Wx-partial, continued dependent types work

53.3 Best Practices

✅ Write top-level type signatures. Compile with -Wall (and -Werror in CI).

✅ Use Text for text, Map/Set/Vector for collections, and Data.Map.Strict.

✅ Model your domain with ADTs and newtypes. Make illegal states unrepresentable. Parse, don't validate.

✅ Prefer total functions. Use Maybe/Either/NonEmpty and avoid head/fromJust/read.

✅ Keep IO at the edges. Keep business logic pure and testable.

✅ Use strict accumulators and strict fields to avoid space leaks.

✅ Use QuickCheck/Hedgehog for properties and laws, and Hspec/Tasty for unit tests.

✅ Format with Ormolu/Fourmolu, lint with HLint, document with Haddock.

✅ Use async for concurrency and STM for shared state.

✅ Choose the simplest abstraction that works ("Simple Haskell", "boring Haskell") and add type-level machinery only when it pays off.

❌ Partial functions and incomplete patterns ❌ Lazy IO in production code (readFile on large or long-lived files)

❌ String for performance-sensitive text ❌ Orphan instances and overly clever point-free code

❌ Catching SomeException indiscriminately (it includes async exceptions) ❌ Huge transformer stacks without need