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:sLift
and simply [| quote |] the expression that the parse resulted in - Deriving
Data
andTypeable
for their equivalent ofExpr
, and then applydataToExpQ
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).