Maximum Segment Sum, Monadically (distilled tutorial, with solutions)
The maximum segment sum problem is to compute, given a list of integers, the largest of the sums of the contiguous segments of that list. This problem specification maps directly onto a cubic-time algorithm; however, there is a very elegant linear-time solution too. The problem is a classic exercise in the mathematics of program construction, illustrating important principles such as calculational development, pointfree reasoning, algebraic structure, and datatype-genericity. Here, we take a sideways look at the datatype-generic version of the problem in terms of monadic functional programming, instead of the traditional relational approach; the presentation is tutorial in style, and leavened with exercises for the reader.
💡 Research Summary
The paper revisits the classic Maximum Segment Sum (MSS) problem—given a list of integers, find the largest sum among all its contiguous sub‑lists—and shows how to derive a linear‑time solution using monadic functional programming techniques. The naïve specification mss = maximum . map sum . concat . map inits . tails directly translates into a cubic‑time algorithm because it enumerates every possible segment, computes its sum, and then selects the maximum.
The first step is to simplify the expression using standard properties of map, inits, and tails. By pulling the outer maximum inside, the author rewrites the specification as
mss = maximum . map (maximum . map sum . inits) . tails
If the inner part maximum . map sum . inits can be expressed as a right fold foldr h e, then the outer tails can be fused with it via the Scan Lemma, yielding a single scan over the list. The crucial observation is that the fold’s step function h can be made constant‑time, turning the whole algorithm into O(n).
The constant‑time step is obtained by recognizing a semiring structure underlying the problem. In the “tropical” semiring on integers, addition is the binary maximum (⊔) and multiplication is ordinary integer addition (+). With this interpretation,
maximum . map sum . inits = foldr (⊕) 0
where u ⊕ z = 0 ⊔ (u + z). This is precisely Horner’s rule for evaluating a polynomial, now applied to a semiring rather than the usual (addition, multiplication) pair. Because ⊕ is constant‑time, the scan yields a linear‑time MSS algorithm.
Having obtained a clean list‑based solution, the paper proceeds to generalise the whole construction to arbitrary recursive data types of the form µ(F α). The notion of a “tail segment” becomes a labelled variant L α = µ(G α) where each node carries a single label; subterms computes the tree of sub‑terms, and a datatype‑generic scan lemma shows that scan F f = L (fold F f) . subterms F.
The dual notion of an “initial segment” is modelled by extending the functor with a unit: H α β = 1 + F α β. To capture the collection of all possible initial segments, a monad M is introduced. M represents a collection type (lists, bags, sets) equipped with a binary union ⊎ and an empty element 0. The pruning operation
prune = fold F (M in H . opt Nothing . M Just . δ₂)
non‑deterministically replaces any sub‑term by the empty structure, thereby generating every initial segment. opt inserts an optional element, while δ₂ distributes the shape functor F over the monad.
The central algebraic law is the distributivity of “product over sum”. Given an F‑algebra (β, f) (the generic product) and an M‑algebra (β, ⊕/) (the generic sum), the law
⊕/ . M f . δ₂ = f . F id (⊕/)
states that folding the product first and then summing yields the same result as summing first and then folding the product. This law allows the whole computation to be fused into a single fold:
fold F ((b ⊕) . f)
where b is the value assigned to the empty structure (its choice does not affect the final result).
Consequently, the datatype‑generic MSS algorithm can be expressed as
MSS = ⊕/ . contents L . scan F ((b ⊕) . f) . tails
All constituent operations (tails, scan, contents, the binary ⊕) run in constant time, so the overall complexity remains linear in the size of the input structure.
The paper interleaves a series of exercises (with solutions) that guide the reader through each transformation: deriving the fold for maximum . map sum . inits, applying the Scan Lemma, defining subterms and scan for arbitrary functors, constructing the pruning monad, and proving the distributivity law. These exercises reinforce point‑free reasoning, paramorphic composition, and the use of monad laws in concrete algorithm design.
Beyond the specific problem, the work illustrates a broader methodology: building domain‑specific languages as libraries inside a host language (Haskell) by combining combinators, algebraic laws, and datatype‑generic programming. It demonstrates how high‑level abstraction, when grounded in solid algebraic structures such as semirings and monads, can yield both elegant specifications and efficient implementations. The result is a compelling case study in the mathematics of program construction, showing that sophisticated reasoning can coexist with practical, optimal code.
Comments & Academic Discussion
Loading comments...
Leave a Comment