Compile-time evaluation

Contents
  • GHC language extensions
  • Splicing
    • Runtime checking with error
    • Compile-time checking with splices
    • Runtime checking with fail
    • Using MonadFail in a splice
  • Quasi-quotation
  • Complete example

If you replace an expression expr with $(lift (expr)), then that expression will be evaluated during compilation. The type of expr must have an instance of the Lift class, which comes from the template-haskell package, and the TemplateHaskell extension must be enabled.

If you have reason to be concerned that evaluation of expr may fail, this technique has the benefit of giving you a compilation failure instead of producing a program that fails by throwing an exception at runtime. For example, in this article we define a type named DigitThe example in this article is based on the d10 package. Other libraries that use this technique include modern-uri and aeson-qq. which is a newtype for Int – with the restriction that the value must be between 0 and 9 – and we discuss ways to write constant expressions in Haskell signifying “the digit n” such that “the digit 5” compiles but “the digit 12” does not.

For further convenience, if the expression expr is of the form f "..." (some function applied to a string literal), then you may want to consider defining a quasi-quoter.

g :: QuasiQuoter
g = QuasiQuoter { quoteExp = \str -> lift (f str) }

Then instead of writing $(lift (f "...")), you can equivalently write [g|...|]. To use the quasi-quoter, the QuasiQuotes extension must be enabled.

GHC language extensions

These features require some GHC language extensions:

  • An expression of the form $(...) is called a splice. It is enabled by the TemplateHaskell extension. When a library has a module containing functions that are designed to be used in splices, that module is conventionally named TH.

  • An expression of the form [quoter|string|] is called a quasi-quotation. It is enabled by the QuasiQuotes extension. When a library has a module containing quasi-quoters, that module is conventionally named QQ.

  • You may also need the DeriveLift extension, Alternatively, you may derive Generic and use genericLift from the lift-generics package instead of lift. which lets automatically derive a Lift instance for a type that you have defined.

Sign up for access to the full page, plus the complete archive and all the latest content.