Compile-time evaluation
- GHC language extensions
- Splicing
- Runtime checking with
error
- Compile-time checking with splices
- Runtime checking with
fail
- Using
MonadFail
in a splice
- Runtime checking with
- 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 Digit
The 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
= QuasiQuoter { quoteExp = \str -> lift (f str) } g
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 theTemplateHaskell
extension. When a library has a module containing functions that are designed to be used in splices, that module is conventionally namedTH
.An expression of the form
[quoter|string|]
is called a quasi-quotation. It is enabled by theQuasiQuotes
extension. When a library has a module containing quasi-quoters, that module is conventionally namedQQ
.You may also need the
DeriveLift
extension, Alternatively, you may deriveGeneric
and usegenericLift
from thelift-generics
package instead oflift
. which lets automatically derive aLift
instance for a type that you have defined.