Template Haskell: From AST to DSL

Good day!

PhD student here, I have been working with Template Haskell (TH) to generate specialised code and observe potential speed-ups at runtime.

After 3 small-to-medium sized projects/experiments, I found it quite boring to build TH ASTs manually, and the code base ended quite cluttered and unpleasant to read (might be a skill issue though).

For a side project, I played around with lenses and the State monads, which when mixed together, allow interacting this the state object in an imperative way (e.g. see operators like (+=)).

So with a bit of code generation, I came up with template-haskell-natural, a DSL that allows building TH ASTs through monadic operations. The library is named that way because writing the meta-program feels natural.

For example, a code generator that outputs the the identity function (along with its type signature) could look like this:

import qualified Language.Haskell.TH.Natural.Syntax.Builder as B 

genId :: DecsQ
genId = genDecs $ newFun "myId" $ B.do
	-- Optional
	setSignature $ newSignature $ B.do
		a <- liftB $ newTypeVar "a"
		addParam a
		setReturnType r
    bodyFromExpr $ newExpr $ B.do
		a <- arg 
		returns a

This particular example may feel a bit verbose, but you can find more byte-sized examples in this blog article.

TLDR, the library allows:

  • Building ASTs for type classes, instances, data types and function declarations
  • Building ASTs for lambda and do expressions
    • Including pattern matching and deconstruction
  • (Limited) support for typed expressions

Internally, the library generates standalone constructors and lenses for each field. It also provides a wrapper layer (Builder) to ensure (to a certain extent) that the AST to produce is somewhat valid (e.g. that the last statement in a do expression is not a bind).

From the consensus I could read online about Template Haskell, it’s usually avoided/disliked because of it’s unstable API and how it impacts the compilation process.
But I would be curious to see what Template Haskell users think of this approach. Feedback would be appreciated :slight_smile:

Thank you!

19 Likes

Perhaps name it th-builder then?..

3 Likes