SkillAgentSearch skills...

Goji

An OCaml bindings generator for JavaScript libraries

Install / Use

/learn @klakplok/Goji
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Goji: OCaml-JavaScript bindings generator

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

For now, its is able to:

  • Generate bindings from high level descriptions
  • Grab JavaScript dependencies and include them in you application code

Its main features are:

  • An OCaml-centric approach, including the possibility to map complex data types and to use OCaml's optional and labeled arguments
  • Concise ans high level descriptions based on lenses
  • Meta-generation of binding descriptions (built as an embedded DSL)
  • Customizable generation scheme, in particular to handle events either by hand or with your concurrency library of choice
  • Some static checking and optional insertion of dynamic cheks for debugging purposes
  • Automated handling of JavaScript dependencies

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

HOWTO start writing your first library binding

  • Build and install Goji. This installs a goji command and two OCamlFind packages: goji_lib and goji_runtime. Among others, Goji depends on the pprint package. The pprint package is available in opam (http://opam.ocaml.org/). However, Goji does not build with the latest version (see https://github.com/klakplok/goji/issues/1). As a workaround, you can pin pprint to the version 20130324 like that opam pin pprint 20130324.
  • Bindings descriptions must be defined in OCaml source files, using binding description pimitives provided by the goji_lib package. In other words, you use predefined OCaml functions and types to build an intermediate representation of your binding. Goji then takes this intermediate representation to generate your binding as new OCaml source files (along with METAs, Makefiles, etc.). This is similar to CamlIDL, and different from ocaml-ctypes. So open up a new OCaml source file in which to write your description, for instance with the same name as the library you want to bind.
  • The first step is to declare a package for your library using Goji.register_package, providing its version and a short documentation. Once done, you can start building the architecture of your library by adding top level modules using Goji.register_component. If you have a lot of toplevel modules, you can define them in separate OCaml source files for convenience, but you'll have to be careful abour their order when feeding them to goji. You are free to architecture your binding as you wish, in particular, you do not have to respect the structure of the underlying library.
  • For each component, you have to define meta-information. Some are optional, such as your name or OCamlFind dependencies. Some are required, such as the license. This is required because most of the time, you will include some parts of the original library, in particular in documentation strings, and in such case, you have to respect the original license. Goji.License contains predefined licenses, and you can define your own. You can also explain how to obtain the sources of the JavaScript library by using Goji.Grab. This is explained in a dedicated section below.
  • Describe the internal structure as a list of toplevel binding elements (type Goji.AST.binding). You can define sub-modules, types and values as described in a following section.
  • When you are ready and that your descriptions successfully compile against the goji_lib package, you can feed them to the goji generate command. By default, this will produce a folder for each of the packages you registered, with the generated sources, METAs and Makefiles inside. You can call goji generate --help for more options.

Writing binding descriptions using the AST or the DSL

You have two options to build binding descriptions.

  • Directly write values from Goji's intermediate representation (AST nodes), by calling the constructors defined by the various types of the Goji.AST module. The AST is made public, documented and should be fairly stable. Even if you don't use it directly, it is a good idea to browse its definition for understanding how descriptions are structured.
  • The AST has been designed for being actually writable by hand, but since it encode features only needed by complex bindings, writing simple descriptions can be a little verbose or confusing. For this, the Goji.DSL module defines functions that correspond to AST constructors but with some parameters made optional. For instance, documentation can be passed with ~doc:"..." or omitted (but should not).

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.

  • Type definitions explain how an OCaml data type is mapped to a JavaScript data structure, as explained in the following section. It produces an OCaml type definition and two conversion functions and two converters for internal use (these functions appear in the signature for cross-library interoperability, but are omitted for the OCamlDoc).
  • Function definitions explain how to map an OCaml function to some JavaScript primitive (not mandatorily a JavaScript function call). A later section is dedicated to this topic.
  • Some object oriented features such as inheritance between types and method definitions are also available. By default, these do not produce object oriented code. Inheritance produces explicit corecion functions and methods produce functions to whom the object is passed as parameter. An object oriented back-end is planned in which an abstract type, associated methods and inheritance declarations are actually mapped to an OCaml class. It is thus a good idea not to forget to these primitives.
  • Local exceptions can be declared, so they can be raised either when a JavaScript exception is thrown in an external code or when some value cannot be converted (see the section on guards).
  • The structure of the OCaml code can be defined by using the Structure AST node, which produces a submodule. The Section node simply adds a documentation header to a series of bindings. The Group constructor is on the other hand completely transparent, its content is flattened into its parent. This is useful for writing macros that have to produce several AST nodes but must only return one.

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:

  • The injector takes an OCaml value and converts it to JavaScript. In the case of type definitions, the result is a single JavaScript value, but in other contexts, various effects can be performed in the JavaScript world (for instance assigning function parameters or globals), hence the name. We say that the function injects an OCaml value in the JavaScript context.
  • The extractor performs the opposite conversion: it extracts an OCaml value from the JavaScript context. In the case of a type definition, this context is actually a single JavaScript value.

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.

  • Goji.AST.value is the top level part of the description. It describes the structure of the OCaml type, for instance Tuple, or Variant. The leaves are described using the Value case, which associates a mapping to a storage to describe the conversion of a single JavaScript value.
  • The Goji.AST.storage gives the location of the JavaScript value. It is basically a path inside the JavaScript context, and in the case of a type definition, a path from the root of the JavaScript value. For insta

Related Skills

View on GitHub
GitHub Stars44
CategoryDevelopment
Updated1mo ago
Forks8

Languages

OCaml

Security Score

75/100

Audited on Feb 2, 2026

No findings