A couple of weeks ago I stumbled across a Template Haskell question on the Haskell sub-reddit. This was quite exciting because I rarely see TH questions, and this one was by someone working on something which was quite similar to what I had done: they were writing a code generator, and had pure functions of type Exp -> Exp -> Exp, but wanted to leverage TH's quasi-quotation syntax (e.g. [| \p -> p + 1 |]) as a syntactic short-hand. Alas these quasi-quotes are of type Q Exp, so the question becomes, how to escape Q?

I had to do exactly this, so I knew an answer: use unsafePerformIO. Another commenter pointed at this Overloaded Quotation Brackets GHC Proposal which I was not aware of and is a much more elegant solution, available since GHC 9.0.1.

There was something odd about the question, though.

Sometime later on, I checked back to find that the thread had been deleted. It turns out it was one of a set of duplicate posts with exactly the same question. Spammers are copying old questions and reposting them, presumably in an attempt to harvest "Reddit karma".

When I learned that, my sense of oddness about the question remained, so I went to find the original thread, to discover I wrote the original question a year ago and had therefore literally answered my own question a year later.

Back to the more elegant solution. In GHC versions 9 onwards, quasi-quotation returns a different type that is not bound up in the Q Monad:

GHC 8.8.4:

λ> :t [| 42 |]
[| 42 |] :: Language.Haskell.TH.Lib.Internal.ExpQ
λ> [| 42 |]

<interactive>:5:1: error:
    • No instance for (Show Language.Haskell.TH.Lib.Internal.ExpQ)
        arising from a use of ‘print’
    • In a stmt of an interactive GHCi command: print it

GHC 9.0.1:

λ> :t [| 42 |]
[| 42 |]
  :: Language.Haskell.TH.Syntax.Quote m =>
     m Language.Haskell.TH.Syntax.Exp
λ> [| 42 |]
LitE (IntegerL 42)

Comments