## Text-based Macro System for F#

One feature I’ve seen repeatedly people wishing is a macro system, so I went ahead and tweaked the compiler to add a text-based macro system. I’m warning here and now: this is only a prototype, it is imperfect and definitely incomplete. The current version uses in-place text replacement on compilation time, using precompiled macros (functions marked with a special attribute).

### Using Macros

Before you can use a macro, you need to tell the compiler in which .NET assembly it can be found. This is done similarly to adding references, but using the –macro-provider flag instead of the –reference flag (-m for short, instead of -r for short). The compiler will search in all classes/modules marked as MacroModule for static methods/functions marked as Macro which match a specific signature (MacroInput -> MacroOutput). Macro usage syntax is <<name {code}}>>. The name can be any legal F# identifier and the code can be any string which does not contain two right curly-brackets immediately adjacent (the curly-brackets can be escaped: }}} goes to }}, }}}} goes to }}} etc.).

There are currently 4 demo macros (I have some others for specific uses I needed):

• LambdaMacro – typed λ-calculus (e.g. <<(λ {{fgy.f(λz.gzy)y}}) : ((‘a –> ‘b) –> ‘c –> ‘d) –> (‘a –> ‘c –> ‘b) –> ‘c –> ‘d>>)
• InlineMacros – C-style replacement macros. Define a macro using <<def {{add(a, b) –> ((a) + (b))}}>> or <<def {{LENGTH –> 8000}}>> and then use the macro <<add {{2 * 3, 4 * 5}}>> or <<LENGTH {{}}>>. It is possible to escape the comma (,). This macro is using the MacroAPI which allows macros to see which macros are available to register additional macros.
• XmlLiterals – XML literals similar to Opa’s XML literals (that is, closing a tag is with </> instead of </name>): <<xml {{<a href="http://fpish.net/org/~<~"c" + string 4 + "fs"~>~">Here is ~<~xml {{{<i>F#</>}}}~>~!</>}}>> (see how the inner XML literal has escaped curly-brackets). FSI responds to this input: <<val it : System.Xml.Linq.XElement = <a href="http://fpish.net/org/c4fs"> Here is <i> F# </i> ! </a> {…}>>.
• LaTeXFormulae – support for basic LaTeX input. To be honest, this is my favourite macro, and it helped me convince several people that F# is awesome. Here are some examples:

LaTeX {{\frac{f\left(x+h\right)-f\left(x\right)}h}}

$\frac{f\left(x+h\right)-f\left(x\right)}h$

LaTeX {{\sum_{i=1}^x{\binom{x}{i}}} }}

$\sum_{i=1}^x{\binom{x}{i}}$

LaTeX {{\sum_{i\in a}{\left(\binom{n^\left(i-1\right)}i+\left|\left\{1,\frac23,3\right\}\right|\right)} }}

$\sum_{i\in a}{\left(\binom{n^\left(i-1\right)}i+\left|\left\{1,\frac23,3\right\}\right|\right)}$

### Writing Macros

Writing macros is simple, but there are some catches (at least in the current prototype). In general, macros have two parts – parsing and code generating. I usually use FParsec and manually generate F# code, but it is also possible to parse with FsLex/FsYacc or any other parser, and it is also possible to create a quotation printer and write a parser which returns an F# quotation. Here is all you’ll need to know in order to write a macro:

• The reference list must be updated to use the modified FSharp.Core.
• The class/module containing the macro/macros must be marked with MacroModuleAttribute (from Microsoft.FSharp.Compiler.MacroProviders).
• The macro itself must be marked with MacroAttribute (from Microsoft.FSharp.Compiler.MacroProviders).
• The macro must be a function from MacroInput to MacroOutput (both from Microsoft.FSharp.Compiler.MacroProviders).
• All non-trivial references (trivial references – those are references which FSC/FSI use) must be statically linked (this will be changed in future versions; I know how, just takes time).
• Generated code should not produce any warnings (it is just terrible ugly).
• Since the code replacement is in-place, the generated code should/must have the same line count as the macro usage (so the warnings/errors of the following code will have the correct positions) – this is no longer correct, see update below.
• Macros can generate warnings (there is a function for that in the MacroInput) and can get the text position (also in MacroInput).
• MacroOutput is a discriminated union of MSuccess (returning a string containing the generated code) and MFailure (returning the error message).

### Conclusions

I believe macros can improve greatly the usability of F#, including sugar for lenses (see here), type classes (see here for an idea) and joinads (see here). In addition to being a great feature for F# 2, F# macros will complete the F# 3 type providers. While the type providers are useful for generating many classes on-demand from files, web and other resources, the F# macros give you similar features in-line. This improves readability if you have many short/medium-length one-time uses of specific macros.

It is possible to tell F# to use the custom F# version, but the design-time error reports (red-blue squiggly lines) always use the built-in F# version. It seems that without the F# team’s help, there is no good solution for this problem (the bad solution being creating custom F# for VS Shell).