let f x y = expr1 in expr2In OCaml calling a function f with arguments x and y is written: “f x y”. Defining the function also uses that form in the pattern above. Calling f returns the value of expr1 and the new identifier f defines the new function in expr2 but not expr1. Identifiers x and y are parameters that denote arguments in expr1, but not in expr2. The entire expression depicted in the line above is an expression yielding what expr2 yields. The keywords let and in thus appear in pairs.
let f x y = x + 2 * y in f 3 5yields 13. The construct is highly parallel to Scheme’s (let ((f (lambda (x y) expr1))) expr2).
The notion of pairs, triples and general tuples pervades the syntax and semantics of OCaml. “x, y” denotes a pair.
let x, y = 3, 6 in x+yyields 9 and
let p = 3, 6 in let x, y = p in x+ydoes too where p is the pair. The text between let and = is called a pattern and can be quite complex. In Scheme this might be (let ((p (cons 3 6))) (+ (car p) (cdr p)))
let a = 3 and b = 4 in a+bgives 7.
let a = 3 and b = 4 in let a = b and b = a in a-bgives 1. The first identifier after a let or an and does not come into effect until after the in.
rec is a keyword in OCaml too and
let rec f x = if x = 0 then 1 else x * (f (x - 1)) in f 8defines the factorial function and yields 40320. In this variation the function f is available before the in.
Being a functional language OCaml has a lambda expression. (fun x -> 2*x) is the function that doubles its integer argument. Thus
(fun x -> 2*x) 4gives 8.
OCaml has infix operators as in “3 + 5”. When the operator needs to be named as a function in other contexts it is written “(+)”. “(+) 3 5” means the same as “3 + 5”. New operators may be defined as component wise addition as in vectors.
let (+^) (a, b) (c, d) = a + c, b + d in (3, 6) +^ (4, 2)gives (7, 8). This code uses several such new infix operators for 2D vectors.
Unlike Haskell, OCaml has constructs with side effects. Many of my programs avoid all side effects but sometimes that is awkward. The construct “expr1; expr2” performs expr1 for its effects and subsequently performs expr2 for both its effects and its value. This is very much like C and many other common languages. The venerable side effect is assignment as in “a := expr1”. None of the values described so far are suitable to the left of :=. Here is a trivial program with assignment:
let x = ref 0 in x := 4; !xx is defined as a place to keep an integer and that integer may change as the program runs. The compiler knows that x is for integers because 0 is an integer. The assignment puts 4 in that place and “!x” says to go get what is stored in that place since the most recent assignment. That line of code is an expression that produces 4. You can not assign to identifiers, only to things that they designate. You cannot declare an identifier without establishing its permanent value. Thus you can’t merely declare an identifier; you must specify what its contents are as it is born.
Arrays are, unfortunately, always mutable and some record (struct) fields are too. If b is an array then b.(3) <- 9 changes the forth entry to hold 9.
Rich libraries come standard with OCaml. “Array.init is a function that comes out of the module concerned with arrays. It makes an array given the size and a function whose yield is the initial valley for each element of the array.
Array.init 10 (fun j -> j*j)produces an array of the squares of integers from 0 to 9.
let b = Array.init 10 (fun j -> j*j) in for k=0 to 9 do Printf.printf "%d %2d\n" k b.(k) donecreates the table and prints it. This code also illustrates the for loops and printing ala C.
There are these sorts of multiple values in OCaml:
let rec pli lst = match lst with a::b -> (print_int a; pli b) | [] -> () in pli [3; 6; 4]One more construct above:
match expr0 with pattern1 -> expr1 | pattern2 -> expr2If the value of expr0 matches pattern 1 then the value (and effects) of the whole expression are that of expr1. The new variables in the pattern, here a and b are bound to the constituent parts of the value of expr0. Ditto pattern2 and so forth.
In the program that inspired this not there is only a minor mention of types:
type tv = float * float;; type zn = {mutable p : tv; mutable v : tv};;introduces tv as the type of a pair of floats and zn as a record with two mutable fields, each of type tv. The code let av r v = r.v <- r.v +^ v defines a function av with the side effect of adding the floating pair value of v to the v field of record r. This magic is called type inferencing.
To use some jargon OCaml has no ‘strong’ contexts. Most language since fortran knew that x in sqrt(x) had to be a floating point number and so if it saw sqrt(3) it converted the 3 to floating point. Thus the context between the parens in sqrt(3) is strong. In OCaml you must write “sqrt 3.” or “sqrt (float 3)”. All contexts are week in OCaml and the type of an expression is determined by the expression, not its context. The +. operator is floating add. If you want to add one to floating x then you must write "x +. 1.". The +. establishes two contexts on either side and the compiler deduces things about the expressions that appear there. The text x +. y informs the compiler that x and y are both floating point. This seems to be enough clue that OCaml programs spend little other hair telling the compiler about types. Some think it is twice too clever but I am undecided.