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 — Eras · 1990
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.
Functional research needed a standard. Haskell committed: purity, laziness, and effects disciplined by the type system.
Monad tutorials for decades, performance that must be profiled lazily, and adoption that curves like its own evaluation.
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).
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.
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 (+) 003 — 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)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.
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)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 sLaziness 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 headThe 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.
primes = sieve [2 ..]
where
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]installbrew install ghc
runrunghc fact.hs
Cardano, Standard Chartered’s analytics, Pandoc (this ecosystem’s Swiss knife) — and monads escaped into every mainstream language.