Tablecloth: a new standard library for OCaml & ReasonML

Tablecloth is an ergonomic, cross-platform, standard library for use with OCaml and ReasonML, which provides easy-to-use, comprehensive and performant modules, that have the same API on all OCaml / ReasonML / Bucklescript platforms.

Tablecloth is not intended to replace your current standard library. Instead, it’s intended to be a smooth layer over different existing standard libraries to make them look the same. A tablecloth, you see. We have recently released version 0.5.

As Tablecloth is in early alpha versions, it does not currently achieve all of its objectives. However, it is pointing in that direction, and contributions are appreciated.

Design of Tablecloth

Dark uses multiple versions of OCaml on the frontend and backend:

  • Our backend is written in OCaml native, using Jane Street Core as a standard library.
  • Our frontend is written in Bucklescript (dba ReasonML).
  • Parts of our backend are shared with the frontend by compiling them using js_of_ocaml, and running them in a web worker.

We discovered that it was impossible to share code between the Bucklescript frontend and the OCaml backend, as the types and standard libraries were very different:

  • Bucklescript uses camelCase by default, while most native libraries, including Core and the OCaml standard library, use snake_case.
  • The libraries in Belt (Bucklescript’s standard library) have different names and function signatures than native OCaml and Base/Core functions.
  • Many OCaml libraries have APIs optimized for pipelast (|>), while Belt aims for pipefirst (|.).
  • Core does not work with Bucklescript, while Belt is optimized for the JS platform.
  • Belt does not work in native OCaml, while Core is optimized for the native OCaml runtime.
  • Belt is incomplete relative to Core, or to other languages’ standard libraries (such as Elm’s).
  • Belt makes it challenging to use PPXes.

Tablecloth’s solution

Tablecloth solves this by providing an identical API for Bucklescript and native OCaml. It wraps existing standard libraries on those platforms, in order to be fast and memory efficient. It is based off Elm’s standard library, which is extremely well-designed and ergonomic. Tablecloth provides separate libraries for OCaml native, js_of_ocaml, and Bucklescript. The libraries have the same API, but different implementations, and are installed as different packages.

The APIs:

  • are taken from Elm’s standard library, which is extremely complete and well-designed.
  • ship as separate libraries via npm and opam,
  • include support for strings, lists, numbers, maps, options, and results,
  • have both snake_case and camelCase versions of all functions and types,
  • are backed by Jane Street Base (the slimmed down version of Core) for native OCaml,
  • are backed by Belt for Bucklescript/ReasonML,
  • use labelled arguments so that can be used with both pipefirst and pipelast,
  • expose the types they wrap so they can be used with existing libraries,

We also have design goals that are not yet achieved in the current version:

  • Many of the functions in the Bucklescript version were written hastily, and could be much more efficient,
  • Tablecloth libraries don’t support PPX derivers well yet,
  • Tablecloth functions currently might throw exceptions,
  • All functions should be well documented, with well-known and consistent edge-case behaviour, but aren’t,
  • All functions should be well tested, but aren’t,
  • We have not yet made a js_of_ocaml-optimized version.

We also hope that we can use this as a testing ground for making elegant and usable standard libraries for Dark.

Contributing

Tablecloth is an ideal library to contribute to, even if you’re new to OCaml, functional programming, or writing algorithms. The maintainers are warm and friendly, and the project abides by a Code of Conduct. There are many small tasks to be done , and each of them are pretty independent without requiring an understanding of a large system. In addition, a small change to a single function can be extremely helpful.

Here are some ways to contribute:

  • point out inconsistencies between different functions in the library,
  • point out an inelegant function signature which could be improved,
  • point out a way in which the library or any of its parts are confusing,
  • report an edge-case or performance problem in one of the functions,
  • add a small test for an edge-case of one of the functions,
  • copy test cases for a function from another language’s standard library,
  • add documentation to a function,
  • improve a function’s documentation by discussing an edge-case,
  • check that a function cannot throw exceptions (and add a note to the function documentation to that effect),
  • add more functions from Elm’s core library: String, List, Result, Maybe, Array, Dict, Set, Tuple, or Basics, or from any of the Extra libraries. You can perhaps use Philip2 to convert them (and their tests!),
  • optimize a function (the Bucklescript version in particular),
  • write a benchmark for a function, or a set of functions,
  • fix testing for Tablecloth on CircleCI,
  • make a plan for the addition (or not) of U-suffixed functions like in Belt,
  • make a plan for adding polymorphic Maps and Sets,
  • design a Regex module,
  • design an Array module, and it’s interaction with the List module,
  • add a js_of_ocaml-optimized version (and perhaps versions wrapping other OCaml standard libraries like Batteries or Containers).
  • add a Caml namespace to allow users to access the builtin OCaml library functions
  • add a Tuple3 module

A contribution which does as little as adding a docstring to a function is extremely useful, and your contributions are valued. If you’d like to contribute but don’t know where to start, open an issue with your thoughts or questions, or contact us on Twitter or by email.