← the guideThe Web Opens

§ 02 — Eras · 1990

Haskell

purity, laziness, and effects disciplined by types

Left: era context, features, and the 101 hello. Right: 3 core concepts, 3 deeper 201 lessons, the factorial specimen, and where Haskell lives today — each column scrolls on its own.

creators
An academic committee, 1990
status
alive (niche) · fintech, compilers, blockchain, ideas-exporter
lineage
← ML

The problem it solves

Functional research needed a standard. Haskell committed: purity, laziness, and effects disciplined by the type system.

The problem it carries

Monad tutorials for decades, performance that must be profiled lazily, and adoption that curves like its own evaluation.

Key features

pure functions
No side effects in the core: a result depends only on its arguments.
lazy evaluation
Values are computed when needed, so infinite structures are perfectly ordinary.
effects in the type system
IO is a type, so the compiler separates the pure core from the impure edge.
pattern matching and guards
Decomposition and conditions are built into the language, not bolted on.

101 — hello, world

Session one is the smallest program that still counts as speaking Haskell: what every tutorial prints first, in the dialect that mattered when the language arrived (1990).

the first program1990
main :: IO ()
main = putStrLn "Hello, world"

101First contact — hello world and 3 ideas that define how Haskell reads. 201 — 3 lessons on mechanisms you meet after the factorial specimen: the sharp edges practitioners actually use.

101 — core concepts

3 fragments, each naming a habit of mind in Haskell. Read the note, then the code — together they are the 101 in one sitting.

01 — pure functions

No side effects anywhere: a function is a definition, not a procedure — so infinite lists are fine.

primes = sieve [2 ..]
  where
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

02 — type signatures first

Write the type, and the implementation often becomes obvious — types as design.

tally :: [Int] -> Int
tally = foldr (+) 0

03 — IO as a type

Effects live in the IO type — the compiler proves which parts of your program touch the world.

greet :: String -> IO ()
greet name = putStrLn ("hello, " ++ name)

201 — going deeper

The 201 assumes you have read the 101 and the specimen. Each lesson is one mechanism — what it is for, how it looks in code, and the trap or payoff that makes Haskell distinctive at scale.

  1. monads and do-notation
  2. type classes and instances
  3. laziness and strictness

monads and do-notation

A monad is a type with a way to sequence computations; do-notation is sugar for bind. Once IO, Maybe and Either click, the rest of the ecosystem follows.

main :: IO ()
main = do
  line <- getLine
  let n = read line :: Int
  print (fact n)

type classes and instances

Classes describe behaviour, instances describe how a type provides it — the mechanism behind Haskell’s overloading, and the ancestor of Rust’s traits.

class Tonnage a where
  tons :: a -> Int

instance Tonnage Ship where
  tons s = shipTons s

laziness and strictness

Laziness makes infinite structures easy and performance problems subtle; strictness annotations, seq and bang patterns are the tools for fighting back.

sum :: [Int] -> Int
sum (x :| xs) = x `seq` sum xs   -- strict in the head

The specimen — factorial

The infinite list: laziness means the primes can be defined as a sieve over all numbers — purity keeps it honest, monads keep effects at the border.

the infinite list1990
primes = sieve [2 ..]
  where
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

Run it today

installbrew install ghc

runrunghc fact.hs

Where it is now

Cardano, Standard Chartered’s analytics, Pandoc (this ecosystem’s Swiss knife) — and monads escaped into every mainstream language.