1

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?

radrow
  • 6,419
  • 4
  • 26
  • 53
  • 1
    The tool you are looking for is Template Haskell https://wiki.haskell.org/Template_Haskell in which I am not skilled enough to give a proper answer. – John F. Miller Mar 20 '19 at 15:02
  • 1
    Why aren't you just defining it as an `Obj` in the first place instead of an `Either Err Obj` value? – chepner Mar 20 '19 at 15:04
  • _You_ may be "sure" that the value will always be a `Right`, but how does the compiler know? It sounds like you're looking for dependent types. – Jezen Thomas Mar 20 '19 at 15:10
  • @chepner Because this is just a "metaphore" of a much bigger computation with error handling. I wanted to give a minimal example – radrow Mar 20 '19 at 17:33
  • @JezenThomas I am not looking for dependent types, but for a metaprogramming solution – radrow Mar 20 '19 at 17:34
  • 1
    "Much bigger computation" and "statically defined" is the combination I'm having trouble with. – chepner Mar 20 '19 at 17:51
  • @radrow You might want to provide more information about the actual problem. For the minimal example I would just say "don't use an `Either` at all," but I take it this is not the case for the larger program? It is probably better if you give more context, rather than us guessing what that context might be. – David Young Mar 20 '19 at 18:30
  • Okay, updating question – radrow Mar 20 '19 at 19:18
  • 1
    I think your "metaphor" is too simplified. I see what you're going for, but to me this looks a lot like an XY problem, in which case you're definitely better off asking about the actual problem you're having in context. – DarthFennec Mar 20 '19 at 19:19
  • Based on your edit, it sounds like you want something like partial evaluation. As @JohnF.Miller said, it does sound like you want Template Haskell. I don't have much experience with it, but maybe [lift](https://www.reddit.com/r/haskell/comments/7yvb43/ghc_compiletime_evaluation/) is what you want. – David Young Mar 20 '19 at 20:05

1 Answers1

1

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.

bergey
  • 3,041
  • 11
  • 17
  • Could you explain why it must be in different module? – radrow Mar 22 '19 at 07:18
  • The [GHC manual](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell) explains "Inside a splice you can only call functions defined in imported modules, not functions defined elsewhere in the same module." A "splice" is the `$()` expression. I think this simplifies implementation, since GHC can compile one module at a time, as it does without TH. [This blog post by Ed Yang](http://blog.ezyang.com/2016/07/what-template-haskell-gets-wrong-and-racket-gets-right/) contrasts TH with less restrictive macro systems. – bergey Mar 22 '19 at 13:10