πŸ— Wiki

OCaml

OCaml

An infamous functional programming language with Haskell. It is widely used across multiple industry.

Unlike Haskell, OCaml is not pure functional programming, and does not use lazy evaluation by default.

1. Install

Using the opam, the OCaml package manager, you can easily install and manage OCaml and packages.

bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)"

Run opam init to set up environment. By default it just creates some files in ~/.opam directory.

1.1. Getting a different version of OCaml toolkit

First, use opam list ocaml or opam switch list-available command to list the available OCaml compiler.

The easiest way to upgrade OCaml toolkit is simply run the command: opam upgrade

opam switch can create and manage isolated OCaml environment, similar to virtualenv of Python. So if you want to update the version of OCaml toolkit, it is highly recommended to create a new switch.

opam switch create ocaml.5.2.0
# If you want to create a named environment,
#opam switch create my-env ocaml.5.2.0

2. Writing code and run it

This is a sample program; Takes input from standard input, prints 1 if the input is leap year.

(* leap_year.ml *)
let is_leap_year year =
  if year mod 400 == 0 then 1
  else if (year mod 100 != 0) && (year mod 4 == 0) then 1
  else 0

in read_int () |> is_leap_year |> print_int

We can simply run ocaml CLI tool to run it.

$ ocaml leap_year.ml
2022
0
$ ocaml leap_year.ml
2024
1
$

2.1. OCaml Bytecode

If you use ocamlc command, you can get bytecode from the original OCaml code.

$ ocamlc leap_year.ml

It will generate three files: .cmi, .cmo, and a.out file.

.cmi file is an inteface file. It does not contain the bytecode itself, but it has type information and module signatures.

.cmo file is an object file.

a.out file contains the actual compiled bytecode. ./a.out command to run the compiled bytecode. If you see the top of the content of compiled file, you might see the file starts with shebang(#!).

$ head a.out
#!/home/ch1keen/.opam/5.0.0/bin/ocamlrun
...

2.2. OCaml Native Executable

ocamlopt command will create a native executable from the OCaml source code.

ocamlopt leap_year.ml

Append -o option to specify the name of output. It is similar to gcc etc.

2.3. OCaml Native Executable (statically linking)

  1. Pass the -cclib -static option to the ocamlopt.

    It will say dlopen() function

    $ ocamlopt -cclib -static fib.ml -o fib
    /usr/bin/ld: /home/ch1keen/.opam/default/lib/ocaml/libasmrun.a(unix.n.o): in function `caml_dlopen':
    /home/ch1keen/.opam/default/.opam-switch/build/ocaml-base-compiler.5.0.0/runtime/unix.c:280: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
    $
  2. Use musl libc instead of glibc.

$ file fib
fib: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=65eb04ad6f8610ff845b5d270bbd8b75eff738fd, for GNU/Linux 3.2.0, with debug_info, not stripped

References:

2.4. Create .cmm script from ocamlopt

Append -dcmm option to generate .cmm file. It might help you debugging the program.

let rec fib(n: int) =
  match n with
  | 0 -> 0
  | 1 -> 1
  | _ -> fib(n-1) + fib(n-2)
cmm:
(data)
(data int 3063 "camlFib__1": addr "camlFib__fib_268" int 72057594037927941)
(data int 1792 global "camlFib" "camlFib": int 1)
(data global "camlFib__gc_roots" "camlFib__gc_roots": addr "camlFib" int 0)
(function{fib.ml:1,11-87} camlFib__fib_268 (n/269: val)
 (if (!= n/269 1)
   (if (!= n/269 3)
     (+
       (+ (app{fib.ml:5,9-17} "camlFib__fib_268" (+ n/269 -2) val)
         (app{fib.ml:5,20-28} "camlFib__fib_268" (+ n/269 -4) val))
       -1)
     3)
   1))

(function camlFib__entry ()
 (let clos/272 "camlFib__1"
   (extcall "caml_initialize" "camlFib" clos/272 ->unit))
 1)

When you look the script closely, it is a pseudo code that looks like the scheme language.

5. See Also