If you've ever worked with JavaScript, you've probably encountered tiny, hyper-focused utility libraries like is-odd. It's a simple function that checks whether a number is odd. But in OCaml, we don’t typically reach for an external library for something this basic.

So why port it?

This post is about demonstrating how we can port an npm library to OCaml using remelange, an experimental JS/TS to OCaml compiler I’m working on. We'll take an existing JavaScript module, compile it to OCaml, and make it available for the OCaml ecosystem.

Why is-odd?

  • Tiny (~20 LOC): Perfect for a quick proof of concept.
  • Popular npm utility: A real-world example with more than 300k weekly downloads.

Step 1: Understanding is-odd

Here’s the JavaScript source of is-odd:

const isNumber = require('is-number');

module.exports = function isOdd(value) {
    const n = Math.abs(value);
    if (!isNumber(n)) {
        throw new TypeError('expected a number');
    }
    if (!Number.isInteger(n)) {
        throw new Error('expected an integer');
    }
    if (!Number.isSafeInteger(n)) {
        throw new Error('value exceeds maximum safe integer');
    }

    return (n % 2) === 1;
};

It:

  • Uses is-number to check if the input is valid.
  • Computes Math.abs(value) % 2 === 1 to determine oddness.
  • Throws an error if the input isn’t a number.

Step 2: Compiling with remelange

Rather than rewriting everything manually, we’ll let remelange generate the OCaml code for us.

Run the following command:

npx remelange is-odd.js > is_odd.ml

This outputs an OCaml version of is-odd.js. Let’s format it for better readability:

ocamlformat --enable-outside-detected-project is_odd.ml

is_odd.ml:

(* COMPILED WITH remelange *)
let is_odd value =
  let n = Int.abs value in
  n mod 2 = 1

Step 3: Generating an Interface File (.mli)

If we want to publish this as an Opam package, we should generate an .mli file to define the module’s public API:

ocamlc -i is_odd.ml > is_odd.mli

This creates is_odd.mli:

val is_odd : int -> bool

This makes the library easier to use and type-safe.

Step 4: Using the Generated OCaml Code

Now that we have our is_odd.ml implementation, we can test it:

let () =
  Printf.printf "%b\n" (is_odd 3);
  Printf.printf "%b\n" (is_odd 4);

To compile and run:

$ ocaml is_odd.ml
true
false

Conclusion

This experiment shows that remelange can automate JavaScript-to-OCaml conversions, even for something as simple as is-odd. It also provides a pathway for porting more useful JavaScript libraries into the OCaml ecosystem.

Would you like to see date-fns or lodash next? Let me know!

I’m posting this on April 1st. But hey, maybe someone actually needs is-odd in OCaml. Who am I to judge?