(Sorry I'm making up the code in this post rather than actually distilling down the real implementations - it probably doesn't run but you can get the idea)
A few times recently I've wanted to output column-like data from haskell: HTML tables in two cases, and CSV in another.
In these two cases, I wanted column headings (<th>
tags in the HTML case; and a CSV heading line in the CSV case.
Previously I've written code that looks roughly like:
mapM_ putHeading ["heading1","heading2","heading3"] forM_ rows $ \(entry1, entry2, entry3) -> do putCell entry1 putCell entry2 putCell entry3
The annoyance here was that nothing ties together the headings and the data values: although in the case of two or three columns, it is relatively simple to see the correspondence, it was getting hard in some wider cases.
Vaguely inspired by lenses, I rewrote some of this code to look like this:
cols = [("heading 1", \(entry1, _, _) -> entry1), ("heading 2", \(_, entry2, _) -> entry2), ("heading 3", \(_, _, entry3) -> entry3) ] mapM_ putHeading (map fst cols) forM_ rows $ \row -> forM_ cols \col -> (putCell ((snd col) row))
What this does is package up the column heading and the code to generate (for each row) the appropriate content. This makes it easier (I hope) to keep the headings and the data aligned. Also, all the boilerplate that you don't see here (putHeading and putCell disguise it) can be shared, with only a new cols defined for each different table.