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, eventlog53.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