
Tim Docker wrote:
data ColDesc rowv = forall a. ColDesc (rowv -> a) ([a] -> a) (a -> String)
calculate :: [rowv] -> ColDesc rowv -> ([String],String) calculate rs (ColDesc valf sumf fmtf) = let vals = map valf rs in (map fmtf vals, (fmtf.sumf) vals)
This code only does the (rowv -> a) evaluation once
Sorry I didn't know of that requirement. The desired sharing can easily be introduced:
data Results = Results { formatted:: [String], aggregated:: String }
type ColDesc rowv = [rowv] -> Results
calculate :: [rowv] -> ColDesc rowv -> ([String],String) calculate rs transformer = (formatted res, aggregated res) where res = transformer rs
rows = ["I","wish","to","define","a","report"]
mapp g f = g . (map f)
cols = [ \rvs -> Results rvs "", \rvs -> let vals = map length rvs in Results (map show vals) (show$sum vals), \rvs -> let vals = map (\s -> length s * length s) rvs in Results (map show vals) (show$maximum vals) ]
values = [ calculate rows col | col <- cols ]
If we just need to format one value, we can write
format_value v transformer = formatted $ transformer [v]
Due to laziness, the aggregate function will not be computed, unless specifically requested.