Pretty-printing (or showing) template-haskell typed expressions

Good evening! I wrote some code which leveraged Template Haskell some years ago, but I did not used Typed expressions, and I’m now wanting to explore them in order to see if I can get some value out of them.

I’m having a lot of trouble getting started. I like to poke at the expression trees in GHCi to get a feel for things, but nothing I’ve tried this evening has let me inspect a Typed expression.

I’m using template-haskell-2.16.0.0 and ghc-9.0.2, both out of date but I don’t think that’s the problem specifically. (These happen to be the versions in the stack snapshot that the program I’m building on top of uses)

Looking at the haddock docs for Typed TH, there’s a relatively early example of pretty-printing a TExp Language.Haskell.TH

It’s presented in an unusual way, to demonstrate an invalid typed-expression being caught:

>>> fmap ppr $ runQ [|| True == $$( [|| "foo" ||] ) ||]
<interactive> error:
    • Couldn't match type ‘[Char]’ with ‘Bool’

however if you adjust that example to have a correctly-typed quasi-quote, you get a more fundamental error

λ> fmap ppr $ runQ [|| True ||]

<interactive>:120:17: error:
    • Couldn't match expected type: Q a0
                  with actual type: Code m0 Bool

In other words, I think this example in the TH docs is fundamentally broken.

Moving on to try and figure out something myself, I first need to get out of the “Code” monad. examineCode :: m (TExp a) seems to fit the bill perfectly. Alas, if I pass the result through any combination of runQ, pprint, etc that I can think of, I get nothing - nothing is printed. No error, just emptiness.

I also can’t find any exposed typed expression constructors, so I can’t figure out manually building a simple expression tree as I have done with TH’s Exp types.

Can anyone give me a clue to get started?

Thanks!

4 Likes

It looks like that documentation has indeed gotten stale.

There is no Ppr instance for TExp typed expressions. For debugging purposes, you can convert from a TExp to an Exp (which has a Ppr instance), using the unTypeCode function.

The stale example that you reference demonstrates a key difference between traditional expression quotes and typed expression quotes: typed expression quotes do not let you construct incorrectly-typed expressions.

λ: :set -XTemplateHaskell
λ: import qualified Language.Haskell.TH as TH
λ: TH.ppr <$> TH.unTypeCode [|| True == $$([|| "foo" ||]) ||]

<interactive>:12:46: error:
    • Could not deduce (Data.String.IsString Bool)
        arising from the literal ‘"foo"’
      from the context: TH.Quote f
        bound by the inferred type of
                   it :: TH.Quote f => f Language.Haskell.TH.PprLib.Doc
        at <interactive>:12:1-59
    • In the Template Haskell quotation [|| "foo" ||]
      In the expression: [|| "foo" ||]
      In the Template Haskell splice $$([|| "foo" ||])
λ: TH.ppr <$> TH.unTypeCode [|| True ||]
GHC.Types.True
it :: Language.Haskell.TH.PprLib.Doc
λ: TH.ppr <$> TH.unTypeCode [|| True == $$([|| False ||]) ||]
GHC.Types.True GHC.Classes.== GHC.Types.False
it :: Language.Haskell.TH.PprLib.Doc

As for constructing typed expressions, I generally use splices and Lift instances. You could build up expressions using the usual Template Haskell constructors and use unsafeCodeCoerce, but that does not provide the same type safety, as the name implies.

1 Like

Thank you for your reply, I really appreciate it! I’m happy to have my hunch (the docs have gone stale) confirmed. I understand the main intent of the example, and that intent still works, as such, but it is a little misleading to have the vehicle of that intent (a supposed “print out the typed expression”) itself be at fault. I’ll see about fixing that.

I did have a look at writing a Ppr instance for TExps (half of it is trivial, of course, call down to ppr on the non-typed Exp) but I’m now a little stuck on getting the type information out. (a topic for a different thread)

1 Like