Question

In a very simplified sense, I have something like the following:

type Runtime a = {- More or less a StateT on top of an Either monad -}

-- The list of strings in Fn is a bunch of parameter names, the values of
-- which are pushed into the state of the runtime before executing the actual
-- function expr
data Expr = Num Int
          | Str T.Text
          | Fn [T.Text] (Runtime Expr)
          | {- Bunch of other constructors -}

eval :: Expr -> Runtime Expr
parseExp :: Parser Expr

Now, I've never used Template Haskell for anything before I decided it'd be convenient to have a quasi quoter for my toy language, so I admit I might be missing something obvious.

But in any case, I started fiddling around a bit with it, followed some tutorials etc. and basically found that everything except how to deal with the Fn constructor was easy.

During my information foragings on the web, I found two general ways people wrote the expression quoter:

  • Making their Expr data type an instance of TH:s Lift and simply [| quote |] the expression that the parse resulted in
  • Deriving Data and Typeable for their equivalent of Expr, and then apply dataToExpQ on the same parser result

In both cases, I ran into complications with the Runtime Expr. For the first case, the problem was that I couldn't figure out how to implement:

instance Lift Expr where
  lift (Fn ps e) = [| Fn ps ...? |]

(I did manage to implement the instance for Data.Text on my own though).

I suppose the real issue is that I simply don't know TH well enough yet, but no amount of tutorials or exmaples so far has helped me get anywhere with this.

In the second case, the issue was that for Expr to be an instance of Data, there also needs to be an

instance Data (StateT (...) (Either ...) Expr) where
  -- Something

My question then, is whether there's an easy way to do this? Or perhaps I should rethink how the functions of my toy language work?

If the latter, any recommendations on how to get equivalent functionality without running them inside a monad? That does seem the intuitive solution, after all, as the run-time environment of the language requires state and error handling (which is what I use Either for).

Was it helpful?

Solution

Your Expr data type doesn't need to be liftable in order for you to build a quasiquoter for it, and in this case, it's impossible to implement a Lift instance. The reason is that you can't "look inside" the Runtime Expr value, since a StateT values are essentially functions and there's no generic way to find out what a function does.

What you need to do is to construct the AST that builds up the Expr "by hand", i.e. without using the [| |]-quoters (or to be more specific, you can use the [| |]-quotes to help you build the AST, but you can't quote the Expr data directly).

So in essence, you need a parser of type

parseExpQ :: Parser ExpQ

which produces the Exp that represents the Haskell code needed to build the Expr. For the Fn constructor, you'd need to either build a do-block using the DoE-constructor or a bind chain using InfixE and >>=.

If this sounds too complicated, your other option is to represent the function body as an expression (or a list of statements, depending on the semantics of your toy language). I.e.

data Expr = Num Int
          | Str T.Text
          | Fn [T.Text] [Statement]

And define the Statement type so that it can be interpreted to produce the same effects that you previously used StateT Either for.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top