This is the first post of a series concerning one of my craziest projects: writing from scratch an Haskell compiler. This post will be about the context of that project and a “mission statement”. Let’s get that show on the road !
Amongst my many projects is an Haskell compiler targeting virtual machines or other such environments. One reason I have this project is because I like Haskell, and writing a compiler for it seems to be a good way to progress in the language. Another reason is that I am curious about compilers, and writing one — even for as peculiar a language as Haskell — seems to be a good way to quench that curiosity.
The project is to write an Haskell compiler, targeting virtual machines beginning with .Net’s one but it may in the future target also the JVM or Parrot. In the future, it might even target more exotic platforms such as WAM, SQL, OpenCL, OpenGL or PostScript. One goal of that choice is portability, another is that this toy compiler not be totally useless.
Objectives, wistful goals & non-objectives
N.B. : In the following, when I talk about an “host”, I am talking about the target for which we generate code, be it .Net’s VM, the JVM or SQL.
We’ll begin with what the compiler should do when it’s finished:
- Compile code conforming to both Haskell 98 and Haskell 2010, with possibility to choose between both standards.
- Compile code using common extensions, be they syntax extensions (e.g. monad comprehensions) or library ones (e.g. Concurrent Haskell).
- Facilitate as much as possible calling to/from host functions.
- Use as much as feasible the host’s standard library to implement Haskell’s one.
- Map as far as is reasonable Haskell’s concepts to the host’s ones (e.g. Haskell’s packages).
The first two points are par for the course in a compiler, but note that points 3 to 5 are here because of an objective to generate code as “transparent” as possible, from the host point of view.
We’ll continue with what the compiler might do if I have the time:
- Have an option to generate code with a non-strict non-lazy evaluation strategy (e.g. a classic call-by-name or a more exotic call-by-future).
- Experiment with code generation (e.g. automatic memoization)
- Facilitate as much as possible other FFI calls.
- Use as much as possible the host’s facilities for debugging, profiling…
- Be able to self-compile a working version of its Haskell parts
- Be able to target from one compiler all other targets
It may take some effort, but it would be awesome to be able to seamlessly interact between lazy and non-lazy code, with both code bases keeping their non-strict semantics and without needing a recompilation of the called code. The calling code might then need informations on the called code evaluation strategy.
P.S. : While the last point is reasonable for hosts such as .Net or Parrot, at the extreme limit OpenCL or OpenGL, I think it is not the case for hosts such as SQL or PostScript. Thus, I wouldn’t hold my breath in having a PostScript-based compiler targeting SQL, for example.
We’ll conclude with what the compiler won’t even pretend to do:
- Be an highly optimizing and efficient compiler: It is a pet project of mine, after all.
- Have a stable behaviour from one target to another: I wouldn’t bat an eyebrow if .Net’s version is a lazy implementation and the JVM’s one is a non-strict non-lazy one.
- Generate interoperable code from one version to another, at least in the beginning.
Yes, that does mean that performance will not be my primary goal: correctness will be difficult enough a goal, I’m afraid.