By now, you should be able to invoke
ocaml and run code
interactively. If you want to install OCaml on your own computer, follow the
To review, you can start the OCaml "interpreter" (or toplevel) by running the
ocaml command in the Linux shell. Simply type
at the prompt and then press return.
In the above example, the dollar sign ($) simply represents the command prompt. You do not need to type this. It indicates that the system is ready to accept your command.
When you run the OCaml interpreter, you will enter an interactive mode similar to the Linux terminal. If everything is set up correclty, you should see something like the following:
OCaml version 4.04.0 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 /usr/local/lib/ocaml/dynlink.cma: loaded /usr/local/lib/ocaml/camlp4: added to search path /usr/local/lib/ocaml/camlp4/camlp4o.cma: loaded /usr/local/lib/ocaml/unix.cma: loaded /usr/local/lib/ocaml/nums.cma: loaded /usr/local/lib/ocaml/site-lib/num-top: added to search path /usr/local/lib/ocaml/site-lib/num-top/num_top.cma: loaded Camlp4 Parsing version 4.04.0 /usr/local/lib/ocaml/site-lib/num: added to search path /usr/local/lib/ocaml/bigarray.cma: loaded /usr/local/lib/ocaml/str.cma: loaded /usr/local/lib/ocaml/site-lib/bytes: added to search path /usr/local/lib/ocaml/site-lib/batteries: added to search path /usr/local/lib/ocaml/site-lib/batteries/batteries.cma: loaded /usr/local/lib/ocaml/site-lib/batteries/batteriesConfig.cmo: loaded /usr/local/lib/ocaml/site-lib/batteries/batteriesHelp.cmo: loaded /usr/local/lib/ocaml/site-lib/batteries/batteriesPrint.cmo: loaded /usr/local/lib/ocaml/site-lib/pa_comprehension: added to search path /usr/local/lib/ocaml/site-lib/pa_comprehension/pa_comprehension.cma: loaded #
So long as you get the pound sign (#) at the end, you should be good to go for now. The rest is information about packages installed for this course that we will be using later.
Again, the pound sign (#) simply indicates that the system is ready for your
command. Here, however, you will enter OCaml commands rather than Linux
commands. For example, you can enter
3 + 3;; and then hit
return to evaluate the mathematical expression 3 + 3.
# 3 + 3;; - : int = 6
When you hit return, you the toplevel gives you some information
about the expression it just evaluated. In this case, it gives you a hyphen, a
colon, the word "int", an equal sign, and the number six. What does this mean?
In general, the toplevel's response is of the form
<type> = <value>. The status is simply an indication of
whether the expression was valid or not. The hyphen indicates that we're good
to go. The type of an expression is a fixed category to which the expression
belongs. In this case, it's an integer. The value is what we're really looking
for - the expression you entered is equivalent to (evaluates to) the integer 6.
If we enter something invalid, we get an error or an exception. For example,
try evaluating the expression
7 / 0.
# 7 / 0;; Exception: Division_by_zero.
In OCaml, an exception is different from an error. An exception indicates an anomalous condition in the values that you entered. Exceptions, unlike errors, can be handled (that is, they are not necessarily fatal). They generally relate to issues caused by specific values. In this case, the problem was that you used 0 - and 0 is the only value that would produce this exception.
An error is slightly different, and probably more common in OCaml. Errors are compilation or type-checking errors. Let's try out a few pieces of problematic code.
# (6 + 3;; Error:Parse error: ";" or ":" or ":>" or ")" expected after [expr] (in [expr]) # 5 + 'a';; Error: This expression has type char but an expression was expected of type int
An error, as you may have guessed, is something which is more fundamentally wrong with your program. Errors relate to generalities - things that are problems no matter what the specific values are. In the first example, the problem is a syntax error - OCaml has no idea how to read what you just told it to evaluate. In the second example, the problem is a type error. The problem is that you can never add a character to an integer, no matter what the value of the integer or the character is.
Related to this is the notion that OCaml is strictly typed. Nothing really
changes its type over the course of a program, and everything has exactly one
3 is always an integer,
'a' is always a
character, and so on. This will be more important when we talk about functions
To exit the OCaml toplevel, enter
#quit (this time, you
do have to type the pound sign) or CTRL-D.
|ls||Show files in folders in current working directory|
|pwd||Print the current working directory|
|cd||Change your current directory|
|mkdir||Create a new directory|
|rm||Permanently delete a file|
|rm -r||Permanently delete a directory and everything in it|
|cp||Copy a file|
|mv||Move a file or directory|
The OCaml toplevel is quite useful for quick, interactive experiments. You may want to know something's type or evaluate a simple expression. But you can't save your work in the toplevel - once you quit, the program is gone. As such, we usually work with files.
In the Linux shell, you are really working in a filesystem. The command
pwd (for "print working directory") will show you your current
"location" within that filesystem. Many of the commands that you run in the
command line are run with respect to your current working directory. For
example, you can view the contents of your current directory using the command
# pwd /home/jacob/ # ls code example.txt
To move around in this system, rather than clicking on folder icons like you
might with a graphical interface, you issue commands to change the directory.
This is done with the
cd (change directory) command.
For example, I might want to go into my
code directory. To do
cd code at the terminal. Now, if I type
pwd, the result will be
# cd code # pwd /home/jacob/code/
cd also assumed that you wanted to enter a directory from your
current working directory. The
code directory, in this example,
was located directly inside my current directory. You can go deeper all at once
by using relative paths. For example, if there is a directory inside of
ocaml, then I could have entered
cd code/ocaml. These can be used to formulate "addresses" for
directories or files. There are also absolute paths which alaways begin
with a forward slash (/), indicating the "root" directory.
If I wanted to get back to my home directory (
/home/jacob), then I
cd /home/jacob - that would work fine. There's
a shortcut to going up a directory though, which is the
directory. This pair of dots indicates the parent directory of wherever the
dots are seen. A single dot (
.) indicates the current directory.
# cd code # pwd /home/jacob/code/ # cd .. # pwd /home/jacob
You may want to make a new directory for your work in this course. This can be
done using the
mkdir (make directory) command. Again, you can use
either relative or absolute paths, but this time the last name in the path will
be the name of the directory that your create.
# pwd /home/jacob/ # mkdir ling4424 # ls code ling4424 example.txt # mkdir ling4424/gettingstarted # ls ling4424 gettingstarted
You can also copy files and directories. This is done using the
command. Again, you can use relative or absolute paths, but this command takes
two arguments: the location of the file you want to copy, and the location you
want to copy it to. For example, let's say I want to copy
code/ocaml/example.ml to my current working directory.
# pwd /home/jacob/ # ls code/ocaml example.ml # cp code/ocaml/example.ml example.ml # ls example.ml
cp command provides a shortcut for keeping the original name
of the file, so I don't have to type out
example.ml both times. In
the target location, I can use
. as a stand-in for the original
# pwd /home/jacob/ # ls code/ocaml example.ml # cp code/ocaml/example.ml . # ls example.ml
To copy a directory, use
cp -r instead.
There are a lot of other commands in Unix, but this should be a reasonable
starting spot. If you come across a new command, you can use the
man (manual) command to learn more about it (for example, enter
man man now).
To actually open and edit a file, you need a text editor. In this course, we
recommend using Emacs (more information
here). Invoke Emacs
emacs -nw myfile.ml where
myfile.ml is the path
to the file you want to open (Emacs will create it if it doesn't exist).
Once you've opened a file, you can enter OCaml code and save the file (C-x C-s) for later. Let's write a simple program to show how this works.
3 + 3
To send code to the toplevel, select the region that you want to send and then enter C-c C-r. The code will be evaluated as if you had typed it into the toplevel. To start setting a region, type C-space. The cursor then becomes the end of your region.
Almost everything in OCaml is an expression which can be evaluated. This
if expressions, which have the syntax
<bool> then <consequent> else <alternative>. Note that
the consequent and the alternative must have the same type, because in OCaml,
every expression has only a single type.
# if 6 > 3 then "hello" else "goodbye";; - : string = "hello" # if 6 > 3 then "yes" else 'n';; Error: This expression has type char but an expression was expected of type string
This becomes useful when handling identifiers in OCaml. It is possible to
assign a value to a name in OCaml using
let expressions. Just like
every other expression,
let expressions have a value. The syntax
let expression is
let <name> = <expression>
in <expression>. The value of a
let expression is the
value of the expression after the
in keyword, with all instances
<name> replaced with the expression after the equal sign.
# let x = if 6 then "hello" else "goodbye" in x;; - : string = "hello"
This kind of expression can also be used to define functions. With
let expressions defined in the toplevel (or defined outside
outside of any other expression), it is not necessary to include
in. The identifier will be bound globally in the current file.
# let greeting time = if time < 12 then "good morning" else if time < 18 then "good afternoon else "good evening";; val greeting : int -> string = <fun> # let current_time = 8;; val current_time : int = 8 # greeting current_time;; - : string = "good morning"
It should be clear how the above function works. The function takes as input a
time (an integer using the 24-hour system) and returns either
"good afternoon" or
depending on the time entered.