Jarlang

A compiler for Erlang targeting the Web & NodeJS

Check us out on GitHub

What is Jarlang?

Jarlang is an Erlang compiler which generates JavaScript for use on the Web and NodeJS

This allows you to make use of all the nicities afforded to you by using Erlang including the Erlang Standard Library, the powerful Erlang concurrency model, data immutability and more!

Need more details?

How does it work?

Jarlang hooks into the Erlang compiler and does some additional processing to generate valid JavaScript.

Jarlang preserves things such as pattern-matching, Erlang's datatypes and more, and as such the resultant JavaScript file can't run in the browser alone.

Jarlang also comes with a runtime which must be included on any webpages intending to run Jarlang code. This runtime essentially tries to emulate the Erlang runtime environment giving us access to all the things we need to run Jarlang code.

You can read up on more details below:

What is currenly supported?

At this point in time, Jarlang can successfully compile much of the Erlang standard library, albeit with some caveats.

Because parts of Erlang are written in C, this code must be manually implemented and extensively tested. Therefore, just because Jarlang can successfully compile Erlang source code, it does not guarentee the resultant Jarlang code will run.

These hand-written modules (and hopefully, in the future, more of the Standard Library) are automatically included in the Jarlang runtime to allow things to run, but right now the only modules included are the io module and the erlang module.

This allows for most Erlang primitives to run and function as expected, as well as pattern matching, process spawning and other things, but there are also large holes in our functionality which have yet to be written or tested thoroughly.

How are we compiling to JavaScript?

The first thing we do is use the Erlang compiler to compile a standard Erlang module to Erlang's intermediate representation called CoreErlang.

We do this so that we don't need to build a parser for Erlang, since the official compiler does a great job at that already. We also benefit a lot from this as the Erlang compiler makes a few optimisations to this generated code.

We then need to get the Abstract Syntax Tree of our generated CoreErlang. We use Erlang's core_scan and core_parse modules to get this for us.

Once we have the CoreErlang AST, we simply translate it node by node into a JavaScript equivalent AST and we rely on existing NodeJS tools to actually generate JavaScript for us.

How are we handling types?

The original plan was to try to just use JavaScript's types in place of Erlang's ones but this idea didn't get very far.

Instead, we settled on simply implementing a new JavaScript class for each of Erlang's datatypes (outside of a few exceptions), to try and ensure as much similarity with Erlang as possible.

To support cross-type comparisons such as [1, 2, 3] > {1, 2, 3}, each Jarlang datatype simply provides its own numeric "weight" which get substituted instead. (Lists, tuples and maps are more complex, but we handle that too.)

The only classes we don't implement ourselves are Port and Fun, since we either have no sane alternative or JavaScript analogue for the former, and because we're just using JavaScript functions for the latter.

Whenever you call an Jarlang module function (be it part of the runtime system or your own compiled code), we detect and automatically convert JavaScript datatypes to their Jarlang equivalent to reduce the cognitive load of having so many new types to work with.

How are we emulating concurrency?

The Jarlang runtime system contains an array of Process class instances, which are Jarlang's implementations of actors.

Each Process has a message-queue, a unique PID, a behaviour, and a scope.

The runtime system also enables us to get a Process instance via providing a PID, which allows us to send messages from Process to Process.

Each Process behaviour is a reference to a function which needs to be executed. The runtime system also sets a timer via setInterval(..., 0) which iterates over any processes which need to be run, and runs them.

That means that the behaviour of each Process runs completely synchronously, just one after the other. This isn't yet helpful or in any way concurrency, but theres more!

The Jarlang compiler intelligently transpiles Erlang code so that any function calls which have a receieve block instead of immediately executing, defer execution until the next tick of the Process timer.

This means that Processes awaiting messages will defer themselves until a valid message is receieved, which then essentially resumes the Process in a synchronous manner. This deferral is simply implemented by changing the pending behaviour of a given Process which gets run the next tick, and is why we need to manually keep track of a scope.

What does the future hold?

There is still much work to be done on Jarlang. It is by no means a production ready product, but whats cool is that as a proof of concept, it works.

Our main pending tasks are getting more of the standard erlang module behaviour implemented which will allow us to support more of the Erlang standard library.

We'd like to take a look at supporting more compile options such as compiling processes into WebWorkers instead of our faux-process model, but we definitely want to support both.

We don't currently optimise the generated JavaScript much at all either. We want to look into making some compile time optimisations ourselves and also potentially relying on Google's closure compiler to do some magic for us.

Lastly, we'd like to make use of Dialyzer type specifications and turn these into Flow annotations or something, but this isn't very important at the moment.

Play and hack around with Jarlang today


Jarlang on GitHub