Basic usage

In OCaml, every piece of code is wrapped into a module. Optionally, a module itself can be a submodule of another module, pretty much like directories in a file system-but we don’t do this very often.

When you write a program let’s say using two files amodule.ml and bmodule.ml, each of these files automatically defines a module named Amodule and a module named Bmodule that provide whatever you put into the files.

Here is the code that we have in our file amodule.ml:

1
let hello () = print_endline "Hello"

And here is what we have in And here is what we have in bmodule.ml:

1
Amodule.hello ()

Usually files are compiled one by one, let’s do it: (using ocamlopt )

1
2
3
$ocamlopt -c amodule.ml
$ocamlopt -c bmodule.ml
$ocamlopt -o hello amodule.cmx bmodule.cmx

or using ocamlc

1
$ocamlc -o hello amodule.ml bmodule.ml

Now we have a wonderful executable that prints “Hello”. As you can see, if you want to access anything from a given module, use the name of the module (always starting with a capital) followed by a dot and the thing that you want to use. It may be a value, a type constructor, or anything else that a given module can provide.

Libraries, starting with the standard library, provide collections of modules. for example, List.iter designates the iter function from the List module.

OK, if you are using a given module heavily, you may want to make its contents directly accessible. For this, we use the open directive. In our example, bmodule.ml could have been written:

1
2
open Amodule;;
hello ();;

Bytecode Versus Native Code
copy from Real World OCaml

OCaml ships with two compilers: the ocamlc bytecode compiler and the ocamlopt native-code compiler.

Programs compiled with ocamlc are interpreted by a virtual machine, while programs compiled with ocamlopt are compiled to native machine code to be run on a specific operating system and processor architecture.

Aside from performance, executables generated by the two compilers have nearly identical behaviors. There are a few things to be aware of. First, the bytecode compiler can be used on more architectures, and has some tools that are not available for native code. For example, the OCaml debugger only works with bytecode (although gdb, the GNU Debugger, works with OCaml native-code applications). The bytecode compiler is also quicker than the native-code compiler. In addition, in order to run a bytecode executable, you typically need to have OCaml installed on the system in question. That’s not strictly required, though, since you can build a bytecode executable with an embedded runtime, using the -custom compiler flag.

As a general matter, production executables should usually be built using the native-code compiler, but it sometimes makes sense to use bytecode for development builds. And, of course, bytecode makes sense when targeting a platform not supported by the native-code compiler. We’ll cover both compilers in more detail in (Chapter 23](https://v1.realworldocaml.org/v1/en/html/the-compiler-backend-byte-code-and-native-code.html)

OCaml toplevel

OCaml toplevel is linked only with a standard library. There’re several options on how to make other code visible to it:

  1. copy-pasting
  2. loading files #use directive
  3. making custom toplevel
  4. loading with ocamlfind

copy-pasting

This self-describing, you just copy code from some source and paste it into toplevel. Don’t forget that toplevel won’t evaluate your code until you add ;;

Loading with #use directive.

This directive accepts a filename, and it will essentially copy and paste the code from the file. Notice, that it won’t create a file-module for you.

amodule.ml:

1
let hello () = print_endline "Hello"

And here is what we have in And here is what we have in bmodule.ml:

1
2
#use "amodule.ml"
hello();;

Loading libraries with ocamlfind

ocamlfind is a tool that allows you to find and load libraries, installed on your system, into your toplevel. By default, toplevel is not linked with any code except standard library. Even, not all parts of the library are actually linked, e.g., Unix module is not available, and needed to be loaded explicitly. There’re primitive directives that can load any library, like #load and #include, but they are not for a casual user, especially if you have excellent ocamlfind at your disposal. Before using it, you need to load it, since it is also not available by default. The following command, will load ocamlfind and add few new directives:

1
# use "topfind";;

In a process of loading it will show you a little hint on how to use it. The most interesting directive, that is added is #require. It accepts a library name, and loads (i.e., links) its code into toplevel:

1
#require "unix";;

This will load a unix library. If you’re not sure, about the name of the library you can always view all libraries with a #list command. The #require directive is clever and it will automatically load all dependencies of the library.

If you do not want to type all this directives every time you start OCaml top-level, then you cam create .ocamlinit file in your home directory, and put them there. This file will be loaded automatically on a top-level startup.