Skip to content

Instantly share code, notes, and snippets.

@jplew
Created June 3, 2020 00:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jplew/ed6ee372d2b039ff033dcfc9239a8872 to your computer and use it in GitHub Desktop.
Save jplew/ed6ee372d2b039ff033dcfc9239a8872 to your computer and use it in GitHub Desktop.
title
Converting from OCaml

Converting a project from OCaml to Reason is straightforward and doesn't require semantic changes. However, there are a few build setup and miscellaneous changes required.

Makefile

If your build system executes explicit build commands, then the easiest way to use Reason with ocamlopt/ocamlc is by adding the following flags to each compilation step:

# intf-suffix tells the compiler where to look for corresponding interface files
ocamlopt -pp refmt -intf-suffix rei -impl myFile.re
ocamlopt -pp refmt -intf myFile.rei

ocamlbuild

There is no official Reason Syntax support for ocamlbuild though you can write custom rules for ocamlbuild that will use .re/.rei files correctly. This diff removes a legacy utility which can be used as a reference.

Dune

If your project uses Dune then Reason syntax is automatically supported. Add a dependency on the Reason package then Dune will pay attention to .re/.rei files in your project.

# If using esy
esy add @esy-ocaml/reason

# If using opam
opam install reason

Converting From OCaml

OCaml code may be (mostly) automatically converted to Reason by using refmt. See refmt --help for more options:

refmt --parse ml --print re yourFile.ml > yourFile.re
refmt --parse ml --print re yourFile.mli > yourFile.rei

Cleaning Up The Output: The converted Reason code may attach [@implicit_arity] to variant constructors, like so: [@implicit_arity]C(1, 2). This is due to the fact that OCaml has the ambiguous syntax where a multi-arguments constructor is expecting argument in a tuple form. So it isn't clear if OCaml's C (1, 2) should be translated to Reason Syntax's C (1, 2) or C 1 2. By default, refmt will convert it to [@implicit_arity]C(1, 2), which tells the compiler this can be either C(1, 2) or C((1, 2)). The "implicit arity" tag ensures the output recreates OCaml's semantics so that it compiles correctly once converted to Reason.

You can easily clean up the refmt output by requesting that [@implicit_arity] tags be stripped. Pass the --assume-explicit-arity to refmt to force the formatter to generate C(1, 2) instead of [@implicit_arity]C(1,2). This approach will generate cleaner Reason Syntax but you may need to selectively convert some C(1,2) variants to C((1,2)) to make the type checker happy.

There are already some internal exception rules to cover the common constructors that require a single tuple as argument so refmt will be convert them correctly without requiring your manual cleanup (e.g., Some (1, 2) in OCaml will be converted to Some((1, 2)) instead of Some(1, 2)).

To provide your own exception list, create a line-separated file that contains all constructors (without module prefix) in your project that expects a single tuple as argument, and use --heuristics-file <filename> to tell refmt that all constructors listed in the file will be treated as constructor with a single tuple as argument:

> cat heuristics.txt
  TupleConstructor
  And
  Or
> cat test.ml
type tm =
  TupleConstructor of (int * int)
| MultiArgumentsConstructor of int * int
let x = TupleConstructor(1, 2)
let y = MultiArgumentsConstructor(1, 2)
module Test = struct
  type a = | And of (int * int) | Or of (int * int)
end;;
let a = Test.And (1, 2)
let b = Test.Or (1, 2)
let c = Some (1, 2)

Then only the constructor identifiers that were listed will be assumed to accept tuples instead of multiple arguments.

> refmt --heuristics-file \
    ./heuristics.txt --assume-explicit-arity \
    --parse ml --print re test.ml
type tm =
  | TupleConstructor((int, int))
  | MultiArgumentsConstructor(int, int);

let x = TupleConstructor((1, 2));

let y = MultiArgumentsConstructor(1, 2);

module Test = {
  type a =
    | And((int, int))
    | Or((int, int));
};

let a = Test.And((1, 2));

let b = Test.Or((1, 2));

let c = Some((1, 2));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment