I wrote a couple of (hopefully) generally useful packages in the course of making this:
nix-eval is a Haskell library which can “eval” code. It uses strings internally, wraps them up to reduce type errors, and uses OverloadedStrings to let us write them without boilerplate. eval invokes GHC in a subprocess to evaluate these strings, using Nix to install any required dependencies. Helper functions are provided for common situations. As a contrived example, we might say:
let unpack' = withPkgs ["text"] (qualified "Data.Text" "unpack")
msg = withFlags ["-XOverloadedStrings"] (asString "hello world")
in eval (unpack' $$ msg)
Here f $$ x generates code for applying f to x, whilst qualified "Foo" "bar" will generate the code Foo.bar and also add Foo to the import list. The with* functions append to the list of required packages, imports and commandline flags.
f $$ x
qualified "Foo" "bar"
The other useful utility is AstPlugin which is a GHC compiler plugin that dumps out the ASTs of all Core definitions to s-expressions (on stderr; since the compiler monad doesn’t provide access to stdout).