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.ngopalan@csel-kh4240-01 (~) % ocaml OCaml version 4.08.1 #
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.# 2 + 3 ;; - : int = 5 #
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:
# 2 + 3 ;; - : int = 5 # 2 + 3;; - : int = 5 #
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.let <name> = <exp> ;;
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 five = 2 + 3 ;; val five : int = 5 # five ;; - : int = 5 #
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.let <fnname> <argname-1> ... <argname-n> = <exp>
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:# let plus x y = x + y ;; val plus : int -> int -> int = <fun> #
# plus 2 3 ;; - : int = 5 #
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.# let rec fact n = if (n = 0) then 1 else n * (fact (n - 1)) ;; val fact : int -> int = <fun> #
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.
# fact (plus five 1) ;; - : int = 720 #
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 what the name of an expression (We will see an example of one more directive in this introduction shortly.)#use <filename>
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.let five = 2 + 3 let plus x y = x + y let rec fact n = if (n = 0) then 1 else n * (fact (n - 1))
Pay careful attention to the response that OCaml displays as it loads the file. It is actually processing the declaration 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. At a later point in the course, we will try to understand more carefully the different kinds of errors that the input can have and also the nuances of including declarations in an interactive session in this manner. However, do try to find out more and to reflect on this matter yourself before that point, so that you can write and debug a few OCaml programs of your own earlier rather than later.# #use "myfirst.ml";; val five : int = 5 val plus : int -> int -> int = <fun> val fact : int -> int = <fun> # fact (plus five 1) ;; - : int = 720
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 rec app l1 l2 = match l1 with | [] -> l2 | (h::l1) -> h :: app l1 l2
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.# #use "lists.ml";; val app : 'a list -> 'a list -> 'a list =#
OCaml has a more convenient way of showing lists of many elements. For example, you can write
[1; 2; 3]for the expression
(1 :: (2 :: (3 :: []))).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 #.# #quit;; gopalan@rishabh (~/teaching/umn/CSCI2041) %
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.
Created by gopalan atsign cs dot umn dot edu. Maintained by ngopalan atsign umn dot edu and evw atsign umn dot edu. Last modified: August 30, 2019.