4

Suppose there's a handy library for parsing things. It exports a function parseThing along with some types and helpers:

module Text.Thing.Parser
   ( Thing ()
   , parseThing
   , ParseError

   -- ...

   ) where

-- ...

data Thing = {- implementation -}

parseThing :: String -> Either ParseError Thing
parseThing = {- implementation -}  

I decide to write a wrapper library which enables parsing things at the compile-time.

{-# LANGUAGE TemplateHaskell #-}
module Text.Thing.Parser.TH where

import Text.Thing.Parser

import Language.Haskell.TH
import Language.Haskell.TH.Quote

thingExpr :: String -> Q Exp
thingExpr src = do
  case parseThing src of
    Left err    -> fail $ show err
    Right thing -> [| thing |]

thing :: QuasiQuoter
thing = QuasiQuoter { quoteExp  = thingExpr
                    , quoteDec  = undefined
                    , quotePat  = undefined
                    , quoteType = undefined
                    }

Compilation fails with No instance (Lift Thing) error, but I cannot add the instance declaration, because Thing is an abstract datatype I don't have access to: it is defined elsewhere and no constructors are exported. And since I'm new to Template Haskell, I can't come up with a solution other than lifting a local variable (if there is any).

So how do I define thingExpr? Thanks.

nameless
  • 1,969
  • 11
  • 34
  • 3
    Only possible if you have a function `fromThing :: Thing -> T` and `toThing :: T -> Thing` where `T` is a type which a `Lift` instance. Note that `parseThing` satisfies the latter, so if `Thing` has a show instance, you can do this. In any case, you can write it as `lift t = [| toThing $([| fromThing t |]) |]` – user2407038 Jul 16 '14 at 07:01
  • Alternatively, you can write `thingExpr = [| either (error "") id (parseThing s) |]` directly. – user2407038 Jul 16 '14 at 07:21
  • @user2407038, `thingExpr s = [| either (error "") id (parseThing s) |]` is basically the same as calling `either (error "") id (parseThing s)` at the run-time, am I wrong? – nameless Jul 16 '14 at 07:38
  • 1
    You are correct. The parsing will still be done at run time. If you have some other functions which satisfy `fromThing`/`toThing`, then you can do the parsing at compile time - but the "marshalling" will have to be done at run time. There is probably some nasty hack you can put together with `unsafeCoerce` but I wouldn't recommend it. – user2407038 Jul 16 '14 at 07:58

0 Answers0