The typical mode in which we run OCaml when developing programs is an interactive one: when we start OCaml up, it gets into what we might call a "read-analyze-evaluate-display" loop. In this interaction loop you can define functions and ask for expressions that use these functions to be evaluated; the "analyze" part of the description refers to the fact that there may be errors in what you present to OCaml, in which case it has to figure these errors out and tell you about them so that you can fix them and try again.
Note the # in the last line above. This is OCaml's prompt: it signals that OCaml is ready for you to present it expressions that it will analyze and calculate for you.
Perhaps the simplest kind of expression that you can type in is an arithmetic one. An example interaction of this kind may be the following:
What has happened here is that the user has typed in the expression 2 + 3, OCaml has calculated the value of the expression as 5, displayed this and it has then become ready for another expression to be input.
A few fine points to note in the above example:
Lets pause for a moment here to consider a common kind of "mistake" one might make: what happens if you forget to type the two semicolons to end your input to OCaml? Not a problem: OCaml will think that you are continuing your input on to the next line so you can type the semicolons there. Similarly, you can spread your input across two lines. The following examples show these two kinds of interactions:
The simplest form for a let declaration is the following:
Generally, when we write something within angle brackets (as we have done in writing <name> for example), we mean it to stand for a category of expressions. Here, <name> stands for a token that is recognized as a identifier (we won't get into the details here of what constitutes such a token, we will use simple examples for which things will be clear even without such an explanation) and <exp> stands for OCaml expressions of the kind we have already seen.
Here is an example of the use of such a declaration:
In this interaction, we have first asked OCaml to associate the name five with the result of evaluating the expression 2 + 3. OCaml tells us that it has done this through the display on the second line. Then we have checked that it has actually done this by asking it to evaluate the identifier five. OCaml responds in the way we have understood earlier.
Let declarations can also be used to associate function expressions with names. The structure of such a declaration is the following
We will understand various aspects of function expressions later in the course. For the moment, it is enough to understand the following: what such a binding does is associate a function with the identifier used for <fnname>. The function is something that essentially produces a value using <exp> after replacing <argname-1>,...,<argname-n> in it by given values.
That was a mouthful! But the concept is very simple, especially when you see it in an example. Here is one.
What the declaration has done is associate the name plus with a function that takes two integers and produces an integer. This is what OCaml is telling us in the display. We can now use the function in further expressions:
The function above was quite simple. Often we will want to write more complex functions. One particular way in which the function might be complicated is that it may be recursive, i.e., it may use itself in its definition. We can still use a let declaration to bind such a function to a name but OCaml wants us to tell it first that we will be using the name in the definition itself. One way to do this is to use the keyword rec in the let declaration. Here is an example.
Suggested Exercise: Try defining fact by using a let declaration without the rec and try to understand the error message that OCaml will give you in this case.
Once we have built up a few associations of expressions with names, we can combine them into a larger expression as the following example shows.
The way to realize the kind of interaction we really want is to construct the program separately in a data file so that it available beyond an interaction session. Then we "load" all the expressions, let declarations, etc., in the data file into an interaction session using a use directive. This way, the program persists beyond an interaction but what it contains can be used in any given interaction.
The structure of a use directive is the following.
This tells OCaml to load what is contained in the indicated file into the interactive session. Note that the initial # is part of what you have to type, i.e. it is part of the directive itself. It is in fact what tells OCaml that what follows is a directive and should not be confused with the name of an expression. (We will see an example of one more directive in this introduction shortly.)
Here is a concrete example of the described process in the context of the few declarations we have considered. Let us supposed that we have put the following lines in a file called myfirst.ml.
We can then load these declarations into an interaction session and use them as shown below.
Pay careful attention to the response that OCaml displays as it loads the file. It is actually processing the declarations in the file one at a time and displaying what it would have done if the declaration had been typed in at the interaction level. You must examine what OCaml displays carefully each time you interact with it this way to convince yourself that it has accepted all the declarations. In particular, OCaml will stop processing your declarations at the first error it finds, which means that your entire collection could be useless unless you can see that all of the declarations have been accepted without errors.
A few further points about the example above:
The idea here is that when this function is used on a particular list l1, the match expression will check what kind of list it is and will pick one of the cases accordingly.
Let us assume that this definition is put into a file called lists.ml. Then consider the following interaction with OCaml:
As expected, OCaml has processed the contents of the file and figured out that it contains a binding for app. More impressively, it has inferred a fairly non-trivial type for app. We will talk about these things more carefully as the course progresses, but, quickly, the expression 'a list represents a list of elements of type 'a for any chosen value for a. Thus, OCaml has figured out that app is a function that takes two lists of the same type of elements as input (they must have the same type of elements because the scope of the 'a is the entire type expression shown) and it produces a list of elements of that type. The same type inference procedure is used in OCaml as in SML and Haskell and we will understand how it works in due course. For now, just be wowed by the fact that these languages can infer non-trivial types and focus on understanding what the type they show you mean.
OCaml has a more convenient way of showing lists of many elements. For example, you can write
for the expression
This is like in SML, except that a semicolon is used as the separator rather than a comma. Also, all these languages allow users to define their own structured data types and they automatically extend matching and type inference to such data. These are things we will study and understand in detail later in the course.
You can use the quit directive as shown below.
Notice that, like the use directive, this directive also starts with a #.
The other way to end an OCaml session is to type the end of input character that happens to be a ^D. This appears a bit abrupt and also harsh but I think OCaml has gotten used to it—we have been parting company with it this way for several years now.