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.