Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

OCaml  Module 1 – Start here: OCaml, opam, dune and the toplevel

Install OCaml 5.5.1 with opam and use the REPL

Install opam 2.6.0 and an OCaml 5.5.1 switch, use the toplevel with ;; phrases and its directives, run commands in a switch and avoid a macOS SDK trap.

  • Beginner
  • 5 minutes
  • Examples run with ocaml 5.5.1, opam 2.6.0, dune 3.24.2 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 5 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Install opam 2.6.0, initialise it and create a global or project-local 5.5.1 switch
  • Use the ocaml toplevel and utop with ;; phrases and the
  • Recognise and work around known platform traps, including the macOS 27 SDK Unix.pipe crash
  • Run commands inside a switch with opam exec and opam env

Before you start

What you’ll learn · Before you start

What you will learn

  • Install opam 2.6.0, initialise it and create a global or project-local 5.5.1 switch
  • Use the ocaml toplevel and utop with ;; phrases and the
  • Recognise and work around known platform traps, including the macOS 27 SDK Unix.pipe crash
  • Run commands inside a switch with opam exec and opam env

Before you start

On this page

Typing 0.1 +. 0.2;; into OCaml’s toplevel prints 0.300000000000000044, while string_of_float (0.1 +. 0.2) gives "0.3": the same number, shown two ways. Before you can try that yourself you need OCaml installed, and the usual way is opam, which keeps each compiler and its packages in a switch of their own.

Below you will follow the setup from installing opam to a working OCaml 5.5.1 switch, type phrases into the toplevel and read its answers, use directives to load files, trace calls and load libraries, choose which switch a command uses, and recognise one known macOS trap before it costs you an afternoon.

opam installs OCaml into switches, one compiler and its packages in each

opam is OCaml’s package manager. It installs compilers and libraries into a opam switch: One OCaml compiler together with its own set of installed packages, kept apart from every other switch; global switches live in the opam root and local ones in a project's _opam folder.: one OCaml compiler together with its own set of packages, kept apart from every other switch. The ocaml.org installation guide gives one command to get opam on each system, brew install opam on macOS, winget install Git.Git OCaml.opam on Windows, and an install script on Linux, followed by opam init. Follow the whole setup, stage by stage:

Interactive · stage by stage From nothing to a working OCaml 5.5.1 switch Follow the stages down the column. Each one names the command and what it leaves behind on your computer.
From nothing to a working OCaml 5.5.1 switch, step 6 of 6. Showing Install opam, opam init, opam switch create, eval $(opam env), ocaml -version. In focus: ocaml -version.or a local _opam switchInstall opambrew, winget or install.shopam initcreates ~/.opamopam switch createcompiles OCaml 5.5.1eval $(opam env)puts the switch on PATHocaml -versionprints 5.5.1

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Get opam itself. Install the opam package manager with Homebrew on macOS, WinGet on Windows or the official install script on Linux, as the ocaml.org installation guide describes.
  2. Initialise opam once. opam init creates the opam root, ~/.opam, and downloads the list of packages from the opam repository. It asks to add a hook to your shell's startup file.
  3. Create a switch. A switch is one compiler with its own set of packages. opam switch create 5.5.1 builds OCaml 5.5.1 into ~/.opam/5.5.1, which takes several minutes.
  4. Or a switch per project. Run inside a project folder, opam switch create . 5.5.1 makes a local switch in the folder's _opam directory, so that project's packages stay apart from every other.
  5. Use the switch in this shell. eval $(opam env) sets PATH and the other variables for the current switch in the shell you are typing into. opam exec -- runs a single command the same way instead.
  6. Check the result. ocaml -version should now print version 5.5.1. If it prints another version or nothing, the switch is not on your PATH yet.

A global switch, opam switch create 5.5.1, lives in the opam root and can be used from anywhere. A local switch, opam switch create . 5.5.1 run inside a project folder, lives in that folder’s _opam directory, which keeps one project’s library versions from disturbing another’s. Here is what opam reports on the computer that records this track, after opam init and opam switch create 5.5.1:

The switch this track uses Python · switch_session.py
from session import run

# What opam knows after "opam init" and "opam switch create 5.5.1": the switch, its compiler and how to use it.
run("""
opam --version
opam switch list
opam switch show
opam exec -- ocaml -version
opam env --shell=sh | grep OPAM_SWITCH_PREFIX
""")

Output

$ opam --version
2.6.0
$ opam switch list
#  switch  compiler                                           description
→  5.5.1   ocaml-base-compiler.5.5.1,ocaml-options-vanilla.1  ocaml-base-compiler = 5.5.1
$ opam switch show
5.5.1
$ opam exec -- ocaml -version
The OCaml toplevel, version 5.5.1
$ opam env --shell=sh | grep OPAM_SWITCH_PREFIX
OPAM_SWITCH_PREFIX='~/.opam/5.5.1'; export OPAM_SWITCH_PREFIX;

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 switch_session.py

There is one switch, 5.5.1, and the arrow marks it as the current one. Its compiler package is ocaml-base-compiler.5.5.1, the plain compiler with no special options.

What opam 2.6.0 changed for your shell

From opam 2.6.0, the shell hook updates the opam folder in PATH in place instead of always moving it to the front, so a folder you put first yourself stays first. The release also deletes build folders sooner to save disk space.

The toplevel reads phrases and answers with each value’s type

The toplevel, which you start by typing ocaml, is OCaml’s read-eval-print loop. You type a phrase: A definition or expression typed into the OCaml toplevel and ended with ;;, which the toplevel answers with a name, a type and a value., a definition or an expression ended by two semicolons, and it answers with the name, the type and the value. Here are four phrases and their answers:

Four phrases and the toplevel's answers Python · phrases_session.py
from session import toplevel

# Phrases end with ;; and the toplevel answers each with a name, a type and a value.
toplevel("""
let x = 6 * 7;;
x + 1;;
0.1 +. 0.2;;
string_of_float (0.1 +. 0.2);;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# let x = 6 * 7;;
val x : int = 42
# x + 1;;
- : int = 43
# 0.1 +. 0.2;;
- : float = 0.300000000000000044
# string_of_float (0.1 +. 0.2);;
- : string = "0.3"

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 phrases_session.py

let x = 6 * 7;; defined a name, so the answer starts with val x; x + 1;; was a plain expression, so the answer starts with -. The float sum printed as 0.300000000000000044 because the toplevel shows enough digits to identify the exact binary value stored, while string_of_float rounds to a short form for people. The ;; is needed only to tell the toplevel that you have finished typing; in a source file it is usually left out.

Key idea

End each phrase with ;;, then read the answer from left to right: the name (or -), the type after the colon and the value after the equals sign.

Checkpoint

Check: opam and switches · Phrases and the toplevel's answers

  1. Question 1 of 2 Two projects need different versions of the same library. What keeps them from disturbing each other?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: A local switch in each project, made with opam switch create . 5.5.1 inside its folder

    A switch is one compiler with its own packages. A local switch per project keeps each project's library versions apart.

  2. Question 2 of 2 The toplevel answers a phrase with "- : int = 43". What does the dash tell you?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The phrase was an expression that defined no name

    Read an answer as name, type, value: val x : int = 42 after a definition, - : int = 43 after an expression.

Directives load files, show signatures, trace calls and load libraries

Lines that start with # are directive: An instruction to the OCaml toplevel itself that starts with #, such as #use to run a file, #trace to show calls or #require to load a library.: instructions to the toplevel itself, not OCaml code. #use "file.ml" reads and runs a source file as if you had typed it. #trace f prints every call of f with its argument and every return with its result. #show List.rev prints a name’s type or signature. And #use "topfind" loads findlib, after which #require "re" loads an installed opam library:

Directives at work: a file, a trace, a signature and a library Python · directives_session.py
from session import files, toplevel

# Directives start with # and talk to the toplevel itself: load a file, trace a function, show a type, load a library.
files("fact.ml")
toplevel("""
#use "fact.ml";;
#trace fact;;
fact 3;;
#show List.rev;;
#use "topfind";;
#require "re";;
Re.execp (Re.compile (Re.Pcre.re "^[0-9]+$")) "2026";;
""")

Output

$ ocaml
OCaml version 5.5.1
Enter #help;; for help.

# #use "fact.ml";;
val fact : int -> int = <fun>
# #trace fact;;
fact is now traced.
# fact 3;;
fact <-- 3
fact <-- 2
fact <-- 1
fact --> 1
fact --> 2
fact --> 6
- : int = 6
# #show List.rev;;
val rev : 'a list -> 'a list
# #use "topfind";;
- : unit = ()
Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads

- : unit = ()
# #require "re";;
~/.opam/5.5.1/lib/re: added to search path
~/.opam/5.5.1/lib/re/re.cma: loaded
# Re.execp (Re.compile (Re.Pcre.re "^[0-9]+$")) "2026";;
- : bool = true

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 directives_session.py

The trace makes recursion visible: fact 3 called fact 2, which called fact 1, and the results came back as 1, 2 and 6. After #require "re", the regular expression library was on the search path and Re.execp answered true for “2026”.

utop, version 2.17.0 in this track’s pins, is an enhanced toplevel with line editing, history and completion. It accepts the same phrases and directives, so everything in this section works in it too; install it with opam install utop and start it with utop.

Keep a scratch file next to the toplevel

Write longer definitions in a file and load them with #use "scratch.ml". Editing the file and loading it again is faster than retyping a function line by line.

Checkpoint

Check: Toplevel directives

  1. One question After #trace fact;; the call fact 3;; printed "fact <-- 3", "fact <-- 2", "fact <-- 1" and then "fact --> 1". What does "fact --> 1" mean?

    Choose one answer.

    Show the answer: One question

    Answer: The innermost call, fact 1, returned 1

    The trace shows recursion unwinding: three calls go in, then the results 1, 2 and 6 come back out.

opam exec and opam env choose the switch a command uses

A shell knows nothing about switches until opam tells it. eval $(opam env) sets PATH and a few other variables for the current switch in the shell you are typing into, so ocaml, dune and the rest come from that switch until you change it. opam exec -- COMMAND runs one command in the switch without changing your shell, which is the safer choice inside scripts. opam switch show names the current switch, and opam list shows the packages installed in it, which is the first thing to check when a library seems to be missing.

The session in the first section used both: opam exec -- ocaml -version reported 5.5.1, and opam env printed OPAM_SWITCH_PREFIX='~/.opam/5.5.1', one of the variables it sets. When several switches exist, opam switch SWITCH changes the default, and a local _opam switch is chosen automatically inside its project folder when opam’s shell hook is installed.

Running a script against the wrong compiler

Wrong: a build script that calls ocaml and assumes the right switch is on PATH. Right: opam exec --switch=5.5.1 -- dune build, or a local switch in the project, so the script uses the same compiler wherever it runs.

One known trap: a macOS SDK that breaks Unix.pipe

Some problems come from the platform rather than from OCaml. On macOS 26 with Command Line Tools whose default SDK is the macOS 27 one, an OCaml 5.5.1 built from source detects the functions pipe2 and dup3, which that SDK marks as available only from macOS 27. The upstream issue 15067 reports the result: Unix.pipe crashes, and so do builds that depend on it. The fix, pull request 15071, is merged for the 5.5.2 bug-fix release.

Until then, the workaround is to build the switch with SDKROOT set to the macOS 26 SDK, as the switch of this track was. This four-line program is the check: it opens a pipe and closes both ends.

pipe_check.ml: does Unix.pipe work here? ML · pipe_check.ml
(* Opens a pipe and closes both ends: the four-line check for the macOS SDK trap. *)
let () =
  let r, w = Unix.pipe () in
  Unix.close r;
  Unix.close w;
  print_endline "Unix.pipe works"
The pipe check on this track's switch Python · pipe_session.py
from session import files, run

# The Unix.pipe check, built against the unix library with ocamlfind, on this switch (built with the macOS 26 SDK).
files("pipe_check.ml")
run("""
ocamlfind ocamlopt -package unix -linkpkg pipe_check.ml -o pipe_check
./pipe_check
""")

Output

$ ocamlfind ocamlopt -package unix -linkpkg pipe_check.ml -o pipe_check
$ ./pipe_check
Unix.pipe works

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pipe_session.py

On this switch it prints “Unix.pipe works”. On an affected build it would crash instead of printing, which tells you to rebuild the switch with the older SDK or to move to 5.5.2 when it is released.

Checkpoint

Check: opam exec and opam env · The macOS SDK trap

  1. Question 1 of 2 A build script must use the 5.5.1 switch whatever the user's shell has set up. Which line fits best?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: opam exec --switch=5.5.1 -- dune build

    Scripts should name their switch. opam exec -- COMMAND runs a command in a switch; eval $(opam env) prepares an interactive shell.

  2. Question 2 of 2 An OCaml 5.5.1 built from source on macOS 26 crashes in Unix.pipe, and the Command Line Tools default to the macOS 27 SDK. What is the workaround the lesson gives?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Rebuild the switch with SDKROOT set to the macOS 26 SDK

    Upstream issue 15067 describes the crash and pull request 15071 fixes it for 5.5.2. Until then, build the switch with the macOS 26 SDK, as this track's switch was, and check with the four-line pipe program.

Practise: switches, phrases and directives

Answer from what the sessions showed. A question about a concept you missed comes back later in the same session, and each answer explains itself:

Practice that adapts to you

Practise this lesson

10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 What is an opam switch?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: One OCaml compiler together with its own set of installed packages

    opam switch create 5.5.1 made the switch this track uses; opam switch list showed it, marked as current.

  2. Question 2 of 10 On a new computer, in which order do the setup steps come?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: Install opam, opam init, opam switch create 5.5.1, eval $(opam env), ocaml -version

    Install opam, initialise its root once, create a switch, put it on PATH, then check the version.

  3. Question 3 of 10 Why did 0.1 +. 0.2;; print 0.300000000000000044 while string_of_float (0.1 +. 0.2) gave "0.3"?

    Choose one answer.

    Show the answer: Question 3 of 10

    Answer: The toplevel shows enough digits to identify the stored binary value; string_of_float rounds for people

    0.1 and 0.2 are stored as nearby binary fractions, so their sum is 0.300000000000000044. The toplevel shows it in full; string_of_float shortens it.

  4. Question 4 of 10 What does ;; do in the toplevel?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: It ends a phrase, telling the toplevel to read and evaluate what you typed

    Type a definition or expression, end it with ;; and the toplevel answers with its name, type and value.

  5. Question 5 of 10 Which directive reads and runs the file fact.ml in the toplevel?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: #use "fact.ml";;

    #use for your own files, #require for installed libraries after #use "topfind", #show for types and #trace for calls.

  6. Question 6 of 10 You want to try the re library in the toplevel, and #require "re";; answers with an unknown directive error. What did you skip?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: #use "topfind";;, which loads findlib and adds #require

    Load findlib with #use "topfind";;, then #require "re";; loads the library, and its modules such as Re can be used at once.

  7. Question 7 of 10 What does eval $(opam env) change?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: PATH and a few other variables of the shell it runs in, for the current switch

    Use eval $(opam env) in an interactive shell, and opam exec -- in scripts that must not depend on the user's shell.

  8. Question 8 of 10 opam env printed OPAM_SWITCH_PREFIX='~/.opam/5.5.1'. What is that folder?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: The switch 5.5.1 inside the opam root, where its compiler and packages are installed

    Global switches live in the opam root, ~/.opam, one folder each; local switches live in a project's _opam folder.

  9. Question 9 of 10 What causes the Unix.pipe crash described in OCaml's issue 15067?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: Building with a macOS 27 SDK makes OCaml use pipe2 and dup3, which macOS 26 does not have

    The fix, pull request 15071, is merged for OCaml 5.5.2. A four-line program that opens and closes a pipe tells you whether your build is affected.

  10. Question 10 of 10 A teammate on another Mac says their OCaml 5.5.1 is fine. How do you check without guessing?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: Build and run the four-line pipe_check.ml on their switch

    A small program that exercises the suspect call is the fastest reliable check; version numbers alone cannot show how a compiler was built.

Interview questions

Warm-up (fresher to 2–5 years): what is an opam switch, and when would you use a local one?

An opam switch is one OCaml compiler together with its own set of installed packages, kept separate from every other switch on the machine. opam switch create 5.5.1 makes a global switch in the opam root, usable from anywhere, and opam switch list shows which switches exist and which is current. A local switch, created with opam switch create . 5.5.1 inside a project folder, lives in that folder’s _opam directory. Use a local switch when a project needs its own compiler or library versions, so that upgrading a library for one project cannot break another, and so that everyone who builds the project gets the same set. Run commands in a switch with opam exec --, or set up the current shell with eval $(opam env).

Two harder questions follow, about a build that uses the wrong compiler and about a crash that only one machine sees. Note your answer first, then open each first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A project builds on your laptop, but the continuous integration job fails with an error about a library version you never installed there. You suspect the job uses a different compiler or switch. How do you find out and fix it?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A program that uses Unix.pipe crashes with a segmentation fault on one developer's Mac but runs on everyone else's, all with OCaml 5.5.1. How would you narrow it down without guessing?

    Model answer, Pro. First step and follow-ups

Flashcards · 7 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What is an opam switch?

    One OCaml compiler together with its own installed packages, kept apart from every other switch on the machine. opam switch create 5.5.1 makes a global one and opam switch list shows them all.

  2. Where does a local opam switch live, and how is it created?

    In the project folder's _opam directory. Run opam switch create . 5.5.1 inside the project, so the project carries its own compiler and packages.

  3. How do you read the toplevel's answer to a phrase?

    End the phrase with ;;, then read left to right: the name (a dash when the value has none), the type after the colon and the value after the equals sign.

  4. What do the directives #use, #trace, #show and #require do?

    #use "file.ml" runs a source file as if typed, #trace f prints each call and return of f, #show prints a name's type or signature, and after #use "topfind", #require loads an installed library. utop accepts them too.

  5. How do eval $(opam env) and opam exec -- differ?

    eval $(opam env) puts a switch on the current shell's PATH; opam exec -- runs one command in a switch, and --switch=5.5.1 names which one, which suits scripts.

  6. What did opam 2.6.0 change about the shell hook and PATH?

    The hook now updates opam's folder in PATH where it already is instead of always moving it to the front, so a folder you put first stays first. The release also deletes build folders sooner.

  7. Which macOS trap breaks a source-built OCaml 5.5.1?

    Built with the macOS 27 SDK, it crashes in Unix.pipe. Building it with the macOS 26 SDK avoids the crash until 5.5.2 fixes it.

Key takeaways

  • opam installs OCaml into switches, each one compiler with its own packages, created globally with opam switch create 5.5.1 or per project in an _opam folder.
  • The toplevel reads phrases ending in ;; and answers with a name or -, a type and a value, showing floats with every digit needed.
  • Directives such as #use, #trace, #show and #require act on the toplevel itself, and utop accepts the same ones with better editing.
  • eval $(opam env) puts a switch on the current shell’s PATH, while opam exec -- runs one command in a switch.
  • A macOS 27 SDK makes a source-built OCaml 5.5.1 crash in Unix.pipe, and building with the macOS 26 SDK avoids it until 5.5.2.

Terms in this lesson

directive
An instruction to the OCaml toplevel itself that starts with #, such as #use to run a file, #trace to show calls or #require to load a library.
opam switch
One OCaml compiler together with its own set of installed packages, kept apart from every other switch; global switches live in the opam root and local ones in a project's _opam folder.
phrase
A definition or expression typed into the OCaml toplevel and ended with ;;, which the toplevel answers with a name, a type and a value.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.