Home

Awesome

Goji: OCaml-JavaScript bindings generator

Goji is a multi-tool for OCaml-JavaScript interoperability.

For now, its is able to:

Its main features are:

Some sample bindings are available at: https://github.com/klakplok/goji-bindings

HOWTO start writing your first library binding

Writing binding descriptions using the AST or the DSL

You have two options to build binding descriptions.

Apart from providing basic constructor functions, the DSL also defines more high level functions which generate complex AST parts for common non trivial bindings constructs (such as binding an enum to a sum type). The idea is to use OCaml as the meta language to encode custom binding constructs as OCaml functions that produce AST parts or combine other DSL constructs.

If you write new DSL constructs which seem useful outside of your specific binding, don't hesitate to ask for their integration.

Top level binding description

The Goji.register_component takes a list of Goji.AST.binding elements. This list contains descriptions of the top level elements of the generated OCaml module.

Describing data mappings

In order for the library user to see only OCaml values, values have to be converted back and forth between their OCaml and JavaScript representations. For simple types, Goji has predefined construct defined by the type Goji.AST.mapping. When converting values of complex, structured types, the binding has to explain the mappings between the elements of the OCaml structure and those of the JavaScript one, though the type Goji.AST.value. This can often be done inline (for instance when describing the return value of a JavaScript function), or in two steps by using Goji's type definition construct and then by to refering this definition by name. This second option is also the only possibility when mapping records or variants.

The goal of a type definition is twofold:

  1. Produce an OCaml type definition / abbreviation which helps having a clean and documented interface.
  2. Explain how a value of this type is converted to a JavaScript value.

The second task is done by attaching two convertion functions to the type:

Conversion lenses

The conversion functions are automatically generated from a single declarative description of the relations between the OCaml type definition and the JavaScript structure. These definitions are OCaml oriented, consistently with the rest of Goji, and are naturally read as extractions. However, they are actually reversible and one definition is enough to generate both converters (and can be seen as a dedicated kind of lenses).

A lens is described using the following three AST node types.

Here are some example lenses and their meaning:

Writing lenses using the DSL

Writing lenses in the DSL is slightly different from writing their AST, using a little trick to increase conciseness. The DSL only provides primitives of types value and storage, plus an infix notation @@ that we call a rerooting operator.

For each basic mapping (e.g. Int), the DSL provides a value of the same (lowercased) name describing a single value of this type (e.g. val int : value = Value (Int, Var "root")).

This way, the same keyword (e.g. int) can be used to describe a standalone value, or on the left of an @@ for its mapping part (e.g. int @@ field root "x").

The @@ operator can actually take any value as its left operand, making some descriptions simpler and / or more concise. For instance, the following tuple [ float @@ field (field root "pos") "x" ; float @@ field (field root "pos") "y" ] could be alternatively written tuple [ float @@ field root "x" ; float @@ field root "y" ] @@ field root "pos" producing the same AST.

Describing functions / methods mappings

TODO

JavaScript dependency handling

For the user, handling of external JavaScript sources basically works as follows. When compiling a program using bindings a b and c, by simply using goji jslink a b c -o mylibs.js, the programmer obtains a single JavaScript entry point mylibs.js to refer to in its HTML source code.

For this to work, binding authors have to explain how to grab library sources in binding descriptions. This is done by writing a script using primitives from the Goji.Grab module which will download / unpack the sources and put them in a specific form, as explained just after. Such a script can be provided for every registered component using the ~grab parameter.

At generation time, goji runs these scripts and packs the resulting files in an archive inside the OCamlFind directory. The jslink command uses OCamlFind to locate these archives, before unpacking them and merging their contents.

Scripts have to respect the following rules:

At jslink time, all archives are extracted into the current directory (unless the option is passed). An exception is made for goji_entry.js files, which are not extracted directly but concatenated into the file specified with -o.

N.B. It is up to the script to ensure that there is no filename clash between libraries, for instance by using subdirectories. This is a bit of a shame, but there is no simple and uniform way to handle modularity in JavaScript, so this task has to be done manually.

License