I am trying to parse some markdown at compile time and hold on to the Html instance it generates.
Normally I would do something like this using a derived Language.Haskell.TH.Lift.Lift instance:
-- Lib.hs                                                                                                                                                           
module Lib where                                                                                                                                                                              
import Language.Haskell.TH                                                                                                                                                                    
import Language.Haskell.TH.Lift                                                                                                                                                               
                                                                                                                                                                                              
data MyNiceType = MyNiceType { f0 :: Int } deriving (Lift, Show)                                                                                                                              
                                                                                                                                                                                              
preloadNiceType :: Q Exp                                                                                                                                                                      
preloadNiceType = do
  -- do some important work at compile time                                                                                                                                                                          
  let x = MyNiceType 0                                                                                                                                                                       
  [| x |]                                                                                    
However, when I try this pattern with a type that contains a Blaze.Html field:
( I am using the extensions TemplateHaskell DeriveLift DeriveGeneric, and the packages template-haskell th-lift and blaze-html)
data MyBadType = MyBadType { f1 :: Html  } deriving (Lift)
I get this error:
    • No instance for (Lift Html)
        arising from the first field of ‘MyBadType’ (type ‘Html’)
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (Lift MyBadType)
Now, it is pretty clear from this error what GHC wants me to do. But I would really avoid having to instantiate Lift (or Data) myself for the Html type.
Is there a way I can avoid it? Or a different approach I am missing here? Or is implementing the instances trivial by some trick I am not aware of?
I am aware that I could just store the markdown source as a Text during compile time and render it at runtime, but I would like to know if there is an alternative.
 
                        
You can try defining manual instances as in the following proof-of-concept. However, I'd suggest doing some objective benchmarking before assuming that this "pre-compiled" markup will perform better than just doing the rendering at runtime.
A general
Lift (String -> String)instance would be "challenging" to define, but we can lift aStaticStringlike so, by getting its string value and then using theIsStringinstance to construct one afresh:Once that's defined, a
ChoiceStringinstance is tedious but straightforward, except for theByteString. You could consider using theLift ByteStringinstance fromth-lift-instancesinstead, or maybe there's an even better one that I don't know about.That leaves
HTML = MarkupM (). TheAppendconstructor forMarkupMposes a problem, since it introduces a newMarkupM btype quantified over anyb. This means that an instance:won't work, because we'll never be able to guarantee the needed
Lift bforAppend. We can cheat by writing an illegalLiftinstance that only works forMarkupM (). Note here that any values of typeain constructors are ignored and assumed to be() :: ().This appears to work for the following example: