I am working on a simple programming language interpreter in Haskell and I have a bit of trouble while defining standard library. I would like it to be defined as a static string at the toplevel and compiled along with my interpreter:
stdLibStr :: String
stdLibStr = "id a := a;;"
parse :: String -> Either Error UntypedModule
typecheck :: UntypedModule -> Either Error TypedModule
-- constexpr
stdLib :: TypedModule
stdLib = either (error . show) id $ parse stdLibStr >>= typecheck
However, model above won't evaluate stdLib
during compilation time. Moreover, it won't give me any feedback on neither parsing nor typechecking error. I would like my interpreter simply not compile if either parse
or typecheck
returns Left
as in the following example:
stdLibString = "≠²³¢©œęæśð"
-- Compilation error: "cannot parse definition"
stdLib = either (error . show) id $ parse stdLibStr >>= typecheck
I was trying to achievie this using fail
while defining QuasiQuotation for my language, but because of some other problems it is not possible to have such a quotation.
How to do it in most convenient way?
As suggested in comments, Template Haskell is the way to do this. The function below handles the two cases:
compileTime :: Lift a => Either String a -> Q Exp
compileTime (Right a) = lift a
compileTime (Left err) = fail err
It can be invoked as $(compileTime (typecheck =<< parse stdLibStr))
. Or it's short enough to inline as either fail lift
instead.
To use this, any function called in the $()
must be defined in a separate module than where it is invoked.