Declarative programming describes a computation through definitions of results and intermediate values instead of prescribing an explicit sequence of instructions. Functional recipes produce values once, so later steps depend on available definitions rather than changing state. section 1.2.2 section 1.5
In Haskell, this style supports reusable, composable code. Pure functions consume inputs and produce outputs without side effects, while monadic programming provides a way to control effects such as file, network, database, and time interactions. section 1.2.2 section 1.2.3
A declarative design typically:
- Breaks a complex problem into smaller definitions. section 4.3.4
- Lets definitions reference one another without specifying their evaluation order. section 4.3.4
- Keeps pure computations easy to test by comparing inputs with outputs. section 1.2.3
Formatting numbered lines
A line-formatting function can define numbers and text, convert missing numbers to empty strings, find the maximum width, pad each number, and combine the results. The final expression assembles the output; no execution order is prescribed. section 4.3.4
Listing 4.10 A function for transforming numbered lines into human-readable strings
prettyNumberedLines :: PadMode -> NumberedLines -> [String]
prettyNumberedLines mode lineNums =
let (numbers, text) = unzip lineNums #1
numberStrings = map (maybe "" show) numbers #2
maxLength = maximum (map length numberStrings) #3
paddedNumbers = map (pad mode maxLength) numberStrings #4
in zipWith (\n l -> n ++ " " ++ l) paddedNumbers text #5
Inspect the complete Haskell definition and follow how each intermediate result contributes to the final formatted lines. section 4.3.4













