Search code examples
haskellmetaprogrammingtemplate-haskell

Eval expression during compilation and treat runtime errors as compilation errors


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?


Solution

  • 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.