Fork me on GitHub

Aplite: an EDSL for speeding up your Haste programs

As the talks from this year’s Haskell Symposium hit YouTube today, this might be a good time for a post about the Aplite EDSL for speeding up the most computationally intensive parts of your Haste programs. Of course, you could just watch the talk instead, or read the paper if you want a more in-depth treatment.

So what is Aplite?

Aplite is an embedded, domain-specific language – EDSL for short – which lets you write parts of a Haskell web application at a significantly lower level than the rest. These parts of the application are then compiled down to a highly efficient JavaScript subset, which generally outperforms both your Haskell code and an equivalent, hand-rolled JavaScript code.

The best part? These low-level fragments integrate seamlessly with the rest of your Haskell program, letting you mix and match high and low level code as needed by your application’s performance characteristics.

Aplite functions run in the Aplite monad, which only supports a small set of low-level constructs:

  • arithmetic, logic and bitwise operations;
  • mutable references;
  • arrays; and
  • conditionals.

Restricting the language to this set of operations allows Aplite to generate code which is guaranteed to avoid the many performance pitfalls of JavaScript. Aplite also supports multiple backends and styles of code generation, as code that runs fast in one browser might not necessarily do so in another one.

Aplite by example

To get a feel for what the language looks like, the following short Aplite function multiplies two square matrices.

type Mat = IOUArray Word32 Double

matMult :: Word32  Mat  Mat  IO Mat
matMult = aplite $ λsize m1 m2 out  do
  out <- newArr (size*size)
  for (0, 1, Excl size) $ λi  do
    for (0, 1, Excl size) $ λj  do
      sumRef  initRef 0
      for (0, 1, Excl size) $ λk  do
        x  getArr m1 (i*size+k)
        y  getArr m2 (k*size+j)
        modifyRef sumRef (+(x*y))
      getRef sumRef >>= setArr out (i*size+j)
  return out

One thing here is especially noteworthy: matMult is imported as a Haskell function in the IO monad, callable from any other Haskell code. This is due to the use of the aplite function, the main interface to and workhorse of the Aplite language.

This little function takes a function in the Aplite monad, compiles it down to efficient JavaScript, inserts marshalling code for arguments and return values, and creates a Haskell stub for calling the newly created JavaScript function. The end result of this process: seamless integration between Aplite and the Haskell host program.

What’s cool about it?

Besides producing highly efficient code and allowing seamless integration between high and low level code, Aplite has a few other properties that are particularly neat. The Aplite compiler runs in the browser, at run time. This lets the Haskell host program consider the browser environment, user input and other factors, and then modify any Aplite functions before compiling them. This idea is known as multi-stage programming, and is generally used to specialize programs to a bunch of factors that are not known at compile time, allowing programs to run faster than without this specialization.

Developers tend to be busy people however, and might not have the time to sit around exploring cool ways to specialize their programs. To make their lives easier, Aplite takes this idea one step further and actually automates the specialization process. Using Aplite’s run time specializer, the developer only needs to provide a small benchmark for each function that they want to specialize. When the specializer is run, Aplite will recompile the function under specialization with all available backends. Each recompiled function is then used to run the user-provided benchmark, and the old, unspecialized version of the function is overwritten with the version that turns out to be the fastest.

Since the specializer can be triggered by the host program at till, an application might start off not using any specialization, realize that it is running too slowly, and attempt to speed up one or more Aplite functions using the specializer depending on the needs of the application. Put simply, Aplite gives developers more tools to speed up their code at run time.

Try it out!

You can download and try Aplite for your own use case today! Remember, however, that this is pretty new research, and so it’s rather rough around the edges.

For a more in-depth look at the language, the properties that make it cool, more examples and a bunch of benchmarks, have a look at the paper.