Bottom value 2026-10-24
An undefined or nonterminating computation in the denotational account of Haskell. A structured value may contain bottom values while retaining a defined outer constructor:
undefined : [] is distinguishable from an entirely undefined list.[ref]Haskell 2010 Language Report. §3.1, Errors
Haskell 2026-10-24
A functional programming language with parametric polymorphism and lazy evaluation. The code examples here use the Haskell 2010 Language Report specification.
Left fold 2026-10-24
A list reduction carrying an accumulator from left to right:
foldl s z [] = z and foldl s z (x:xs) = foldl s (s z x) xs. In a lazy language this does not by itself force each intermediate accumulator; its association and evaluation strictness are separate issues. An infinite input spine prevents this operation from returning a result.[ref] Operator section 2026-10-31
A partially applied infix operator:
(a op) means \x -> a op x, and (op b) means \x -> x op b, subject to Haskell's syntax rules. In particular, (f .) maps a function g to the function composition f . g. Composing such a section with a curried binary function composes at two argument levels: ((f .) . g) x y = f (g x y) on total inputs.[ref]Haskell 2010 Language Report. §3.5, Sections
These solutions follow the original examination of 11 June 2013, code
4208.[ref]Functional programming, design and analysis of algorithms examination paper by the University of Oxford (2013). pp. 2–7, Questions 1–8
Each question's Haskell definitions form a separate namespace; hide redefined Standard Prelude names when compiling them. Unless stated otherwise, arguments are finite and total. Time complexity counts primitive operations; the cost of comparing variable-length words and the growing bit cost of exact integer arithmetic are stated separately where relevant.[ref]The solutions use the original 17 June 2014 preliminary examination, code
4208.[ref]Functional programming, design and analysis of algorithms examination paper by the University of Oxford (2014). original scan, pp. 2–8
Haskell definitions use separate question namespaces; names suffixed F or U distinguish alternative implementations. For Question 4, use import Prelude hiding (foldr, (++), concat, flip) when compiling the definitions together. Unless an infinite input is discussed explicitly, inputs are finite and total. Arithmetic operation counts do not include the growing bit costs of exact integers.[ref]The Haskell type signatures are The wildcard in the first equation matters under lazy evaluation:
take :: Int -> [a] -> [a] and drop :: Int -> [a] -> [a]. These definitions also handle nonpositive counts as the Prelude does:take n _ | n <= 0 = []
take _ [] = []
take n (x:xs) = x : take (n-1) xs
drop n xs | n <= 0 = xs
drop _ [] = []
drop n (_:xs) = drop (n-1) xstake 0 undefined yields [] without inspecting its list argument. The remainder of this question uses nonnegative counts.[ref]This is the 13 June 2017 preliminary examination. Questions 1–4 concern functional programming; Questions 5–8 concern algorithm design and analysis of algorithms. All 35 printed subparts are answered. Source paper: released examination archive, member
a10097w1-1.pdf.The Haskell definitions use separate question namespaces. For Question 1, hide the locally defined names with
import Prelude hiding ((.), zipWith, uncurry); for Question 4 use import Prelude hiding (scanl). Alternative solve definitions are alternatives, not simultaneous duplicate declarations. Assume finite total puzzle inputs; infinite streams and partial values are treated explicitly where relevant. Operation counts separate numeric bit costs from list and comparison steps.[ref]f (foldNat g a Zero) = f a = b = foldNat h b Zerof (foldNat g a (Succ m))
= f (g (foldNat g a m))
= h (f (foldNat g a m))
= h (foldNat h b m)
= foldNat h b (Succ m)The compatibility premise
f . g = h . f justifies the middle rewrite. This proves the theorem for finite total constructor values by structural induction.For the full lazy Haskell interpretation, also handle the bottom value:
foldNat g a is strict in its outer Natural constructor, so its value on bottom is bottom; the assumed strict function property of f makes the left side bottom too. The same zero/successor induction now covers finite partial constructor chains. Pure definable functions preserve the limits of these information approximations, extending equality to infinite Succ chains. This explains the strictness premise; finite total induction alone would not use it.[ref]This is the 12 June 2018 preliminary examination. Questions 1–4 concern functional programming; Questions 5–8 concern algorithm design and analysis of algorithms. Every printed option is answered here. The complete nine-page native paper is preserved in the Oxford examination release, member
a10097w1.pdf; the damaged duplicate rendition is not the source used here.The Haskell definitions below use finite total inputs unless lazy evaluation, strict functions or infinite lazy lists are being discussed. Separate questions use separate namespaces: in particular their
One constructors are unrelated, and the list-conversion function abs in Question 3 is distinct from the numeric Standard Prelude function. Treat alternative definitions of the same function separately. In a module for Question 2, use import Prelude hiding (zip, zipWith); in one for Question 3, use import Prelude hiding (abs, reverse, (++)). Other questions retain the Standard Prelude operations they use.[ref] Costs count constructor, comparison and ordinary arithmetic operations; integer bit costs and evaluating arbitrary element values are separate.Tree is a type constructor, taking the element type parameter a to the algebraic data type Tree a. It is not a value and has no ordinary value-level function type; its kind of a type is * -> *. The other names are data constructors with polymorphic type signatures:One :: a -> Tree a
Two :: Tree a -> Tree a -> Tree aOne constructs a leaf-labelled binary tree with one leaf; Two joins two such trees under a new internal node. Unlike a general binary tree representation with empty child slots, this representation has no empty tree.[ref]Use the usual left-to-right pattern matching order:
zip :: [a] -> [b] -> [(a,b)]
zip [] _ = []
zip _ [] = []
zip (x:xs) (y:ys) = (x,y) : zip xs ysThe result stops when either list ends, so its length on finite total inputs is the smaller input length. Elements remaining in the longer list are discarded.
Strictness is asymmetric. Writing the bottom value as ,
zip is strict in its first list but not uniformly strict in its second: zip undefined ys is undefined, whereas zip [] undefined = []. With a nonempty first list, an undefined second list does make the result undefined. Neither pair component is forced merely to produce the outer result constructor: zip [undefined] [undefined] already has a defined outer cons and pair. This follows from ordered pattern matching and lazy evaluation, not from the behavior on total lists alone.[ref]Haskell 2010 Language Report. §3.17.2, ordered pattern matching; Chapter 9, zip in the Standard Prelude
The right fold replaces the two list constructors by its two arguments; the left fold instead updates an accumulator while traversing the list.
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr c z [] = z
foldr c z (x:xs) = c x (foldr c z xs)
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl s a [] = a
foldl s a (x:xs) = foldl s (s a x) xsThus
foldr is the natural list fold: it preserves the constructor arrangement while replacing [] by z and (:) by c. In particular, foldr (:) [] = id. More generally, the fold factorisation criterion says that the defining equations characterize the corresponding structural recursion. Association and lazy evaluation are separate: the accumulator in this definition of left fold is not automatically evaluated at each step.[ref]Haskell 2010 Language Report. Chapter 9, PreludeList: foldr and foldl
Include nonpositive counts and exhausted input explicitly:
take :: Int -> [a] -> [a]
take n _ | n <= 0 = []
take _ [] = []
take n (x:xs) = x : take (n-1) xs
drop :: Int -> [a] -> [a]
drop n xs | n <= 0 = xs
drop _ [] = []
drop n (_:xs) = drop (n-1) xsThe tests on the count precede inspection of the input list. Thus
take 0 undefined = [], while drop 0 undefined = undefined, exactly as these list prefix and list suffix operations require. No element value is inspected merely to traverse the spine.[ref]Haskell 2010 Language Report. Chapter 9, PreludeList: take and drop
No universally correct sequential Haskell implementation has that guarantee for partial lists. Compare
undefined paired with [v], and [v] paired with undefined, where the element type is arbitrary. A generic program cannot fabricate v; it must eventually inspect an input spine to obtain it. Whichever input it first demands can be chosen to be the undefined one, blocking access to the available element in the other input.In particular, the implementation in part b can emit a defined prefix and then block when alternation reaches an undefined tail. Lazy evaluation postpones demands; it cannot recognize that a demanded computation will never finish. The result is the partial-list interleaving obstruction, not an objection to fairness under the proper-input hypothesis. Additional concurrent primitives would change the computational model.[ref]
Haskell 2010 Language Report. §3.1, errors and nontermination; §3.17, demands made by pattern matching
For the finite total inputs just specified, a recursive implementation isAt a true test, the head belongs to the list prefix and splitting continues on the tail; at a false test, the complete remaining list is the list suffix. Induction proves that the first projection is the longest accepted list prefix and the second projection is the remaining list suffix.
spanP :: (a -> Bool) -> [a] -> ([a], [a])
spanP p [] = ([], [])
spanP p whole@(x:xs)
| p x = let (ys,zs) = spanP p xs in (x:ys,zs)
| otherwise = ([],whole)There is a small partial-value distinction behind the paper's displayed pair identity. The conventional recursive
span is strict in an entirely undefined list, whereas the literal expression (takeWhile p undefined, dropWhile p undefined) exposes an outer pair with two undefined components. The equality as a pair therefore needs the stated defined-input convention, or an interpretation that compares the two projections. This does not affect the finite total answers here.[ref]Haskell 2010 Language Report. §3.17, pattern matching; Chapter 9, definitions of span and the prefix operations
The ordering operations used to decide the partitions require an
Ord a constraint. A function with parametric polymorphism and type [a] -> [a] cannot inspect arbitrary element values to decide which one is smaller; for example, functions themselves need not admit the comparison operations required by this sorting algorithm. A Haskell Ord class instance supplies the comparison method, with its ordering laws a separate semantic assumption.[ref]Haskell 2010 Language Report. Chapter 9, the Ord class and compare method
For finite lists, use structural induction: establish
P []; then, for arbitrary x and xs, assume P xs and prove P (x:xs). The induction hypothesis must quantify over any other parameters that need to change in the step.For all Haskell list values, finite terminating spines are insufficient: also establish
P undefined, and allow arbitrary, possibly undefined, heads in the cons step. This proves the equation for finite partial lists ending in the bottom value. An infinite lazy list is the limit of these finite approximations. Equations between pure definable computable functions are preserved by those limits, so the equation extends to infinite inputs. This last argument requires that admissibility property; an arbitrary logical property of lists need not have it. The distinction between [] and the bottom value is essential.[ref]A call can instead produce an entire run and recurse on the unused input:Here
groupRuns :: Eq a => [a] -> [[a]]
groupRuns [] = []
groupRuns (x:xs) = (x:ys) : groupRuns zs
where (ys,zs) = span (== x) xsspan returns the longest matching prefix of a list and the remaining suffix of a list. Thus x:ys is the first maximal run, while zs begins the next run or is empty. Each recursive call produces one outer output element. The lazy pair binding permits output of the first constructor before the run's endpoint is known. Every finite input position is scanned a bounded number of times, so the time complexity is for unit-cost equality, including output construction.[ref]A strict function satisfies , where is the bottom value representing an undefined or nonterminating computation. This is a semantic implication: obtaining a defined result requires enough definedness of the argument. It does not prescribe an evaluation order or say that every component of a structured argument is evaluated.[ref]
For example, in Haskell,is strict, since every result is , but never needs to inspect its argument. Moreover, lazy evaluation may leave an entire application unevaluated when its result is unused. Strictness is therefore different from the operational statement that an argument is evaluated on every call.
f :: a -> b
f _ = undefined