I've also used macros to execute some expensive data processing at compile-time. I had a fixed dataset containing named sets of strings, and I wanted a program which reads in a set of strings and returns the names of any supersets which appear in the dataset. For example, if the dataset contains 'x = {a, b, c}', 'y = {a, b, d}' and 'z = {a, c, d, e}' then a query of '{a, c}' should return '{x, z}'.
I managed to speed this up a lot by using a (TemplateHaskell) macro to (a) read in the dataset from an external file at compile time, (b) transform it into a structure that has fast lookups and (c) serialise that structure to a Haskell syntax tree. Not only did this make lookups faster at runtime, it also eliminated all the error checking for locating/reading/parsing/etc. of the dataset: any problem would cause a compile error; if compilation succeeded, the resulting program didn't have to know or care about any of that.
I managed to speed this up a lot by using a (TemplateHaskell) macro to (a) read in the dataset from an external file at compile time, (b) transform it into a structure that has fast lookups and (c) serialise that structure to a Haskell syntax tree. Not only did this make lookups faster at runtime, it also eliminated all the error checking for locating/reading/parsing/etc. of the dataset: any problem would cause a compile error; if compilation succeeded, the resulting program didn't have to know or care about any of that.