Many OCaml guides and tutorials refer to the OCaml toplevel, but what exactly is this thing? In short, the toplevel is OCaml’s Read-Eval-Print-Loop (repl) allowing interative use of the OCaml system. You can consider the toplevel an alternative to compiling OCaml into an executable. In this mode, the OCaml system is configured to repeatedly read phrases from input, typecheck, compile, and evaluate them, then print the inferred type and result value.

Any input into the toplevel can span several lines and we terminate toplevel statements with a double-semicolon (;;).

Starting the toplevel

If you have an OCaml system installed you can start a toplevel environment by typing ocaml at a command prompt. You can check that it is working by doing some basic math. The toplevel system prints a # prompt before reading your input.

$ ocaml
# 3 + 4;;
- : int = 7

Starting utop

utop provides an improved interactive to the OCaml toplevel. It supports command history, macro expansion, module completion, and other additions that make OCaml programming easier. After installing UTop you can start it with the utop command to start the interactive environment. When you start typing a command from the utop prompt, you will see automatic completions being presented. Press tab at any time to select a completion.

Using the toplevel

When you start a new toplevel session, no code is loaded — you begin with a complete blank slate which isn’t very interesting. Where this becomes useful is testing code you’ve written and exploring code written by others. To load an OCaml module installed through opam in the toplevel, use the open command:

# open Base;;

By opening Base, we make the definitions it contains available without having to reference Base explicitly. You can do the same for any modules you’ve written in two different ways. First, you can use the #use command to directly include the file into the toplevel. For example, if I have the file hello_ocaml.ml that simply encodes a message:

16:38 $ cat hello_ocaml.ml
let message = "Hello OCaml!"

I can directly include that in the toplevel with #use. The message variable declared in the file becomes available to the toplevel.

utop # #use "hello_ocaml.ml";;
val message : string = "Hello OCaml!"
-( 16:36:37 )-< command 7 >-----------------------------{ counter: 0 }-
utop # message;;
- : string = "Hello OCaml!"

To include the file as a module, rather than directly, use the #mod_use command. In this case, the message variable is available to us prefixed by the module name we loaded (which is the file name with a uppercase first letter).

utop # #mod_use "hello_ocaml.ml";;
module Hello_ocaml : sig val message : string end
-( 16:47:26 )-< command 1 >-----------------------------{ counter: 0 }-
utop # Hello_ocaml.message;;
- : string = "Hello OCaml!"

Lastly, you can compile your file into bytecode, and load the associated bytecode into the toplevel through the #load command.

$ ocamlc -c hello_ocaml.ml  # compiles hello_ocaml.ml into hello_caml.cmo
$ utop  # start utop
-( 16:50:10 )-< command 0 >-----------------------------{ counter: 0 }-
utop # #load "hello_ocaml.cmo";;
-( 16:50:11 )-< command 1 >-----------------------------{ counter: 0 }-
utop # Hello_ocaml.message;;
- : string = "Hello OCaml!"

This should get you started using the Ocaml toplevel to interact with your code.