Introduction To Ocaml Programming: Unit
Introduction To Ocaml Programming: Unit
In fact, functions are “first-class” citizens. They can be passed as arguments to other
functions, and returned as results of functions. They can be incognito — that is, we can
define and use functions without naming them (just as we can use values such as the
integer 3 without giving it a name).
true;;
- : bool = true
false;;
- : bool = false
not true;;
- : bool = false
not false;;
- : bool = true
false || false;;
- : bool = false
false || true;;
- : bool = false
Experiment: What happens if one does not put the parenthesis around the infix
operators?
"Hello, world!" ;;
- : string = "Hello, world!"
't' ;;
- : char = 't'
'\n' ;;
- : char = '\n'
"Hello" ^ " World!\n" ;;
- : string = "Hello World!\n"
Note that myBool is a completely new type, distinct from any type that OCaml has in its
library. The particular type consists of two distinct values, which are given by the
constructors T and F. As noted above, constructors in OCaml must begin with a Capital
letter. The constructors are separated by a single vertical bar |.
(Note: The constructors can be variously seen as function/constant symbols that evaluate
to themselves, or as colours or tags on the unit value() which is suppressed. An
alternative definition of the type could have been:
type myBool' = T' of unit | F' of unit ;;
type myBool' = T' of unit | F' of unit
T'() ;;
- : myBool' = T' ()
F'() ;;
- : myBool' = F' ()
Let us now define functions from the type myBool. The keyword to indicate a definition of
a value or function is let. A function definition begins with this keyword let which is
followed by the name of the function being defined (this can be any identifier beginning
with a small letter, , and which is not a keyword or a reserved word) followed by 1 or more
parameters to the function. Then we have the equality sign = , and to the right of which
we have the body of the function — an expression that defines the result of the function.
There is nothing very interesting here, except that you have to note that we are trying to be
scrupulous about separating “syntax” from “semantics”, where we are using the standard
OCaml type bool and its values true, false and its operators as the mathematical
booleans (the semantics) whereas the new data type myBool is “syntax”.
Note that we have nowhere indicated the type of the parameter, of the function, or of the
return values. OCaml is remarkable in that the interpreter is able to infer that the
parameter b is of our newly defined type myBool, and the possible results must be of type
bool (OCaml’s bool type) , and so the function myBool2bool being defined is of type
myBool -> bool. (Note that the interpreter treated the function as a value).
How did it manage to do so? There is a type-checking engine with OCaml that inspects the
different cases, and solves constraints (somewhat like how a simultaneous equation solver
works), and works out the type of the parameter from the case analysis, and the type of the
result from the type of the expression on the right side of the patterns. Of course, if there
are inconsistencies, it reports a type error.
Note also that the OCaml interpreter does not try to print out the canonical representation
of this function, and demurely reports its value to be a function (indicated by ). This is
because it would be wrong to presume that there is a canonical representation of functions
(called “intensions”) since a single extensional form of a function can be coded with
multiple intensional forms.
In this function, the case analysis on the parameter is over the OCaml built-in bool values
true and false which appear to the left of the case analysis arrow.
Let us now define a toy myBool calculator, by defining “syntactic algorithms” encoding the
truth tables, but within the user-defined type myBool.
We perform case analysis on the first argument b1, and if it matches the myBool
constructor T, returns whatever results from evaluating the second argument b2.
Otherwise the result is F.
Note that myAnd takes two parameters b1 and b2. These are given one-by-one (in that
order) separated by spaces — and not as a pair (b1, b2). This style is called a “Curry-ed”
style of passing arguments (after the logician Haskell Curry). It is a standard style used in
mathematics and higher-order functional programming.
Note how the type of the defined function is reported: it is a function that takes a single
myBool input b1 and returns a nameless function which is of the type myBool ->
myBool (this unnamed function takes a single input b2 of type myBool and returns an
answer of type myBool). In other words, the types with arrows should be parenthesised
with right associativity: myBool -> (myBool -> myBool)
Again OCaml is able to infer the type of the function from the case analysis and the type
constraints collected without (painful) type annotation by us.
Get used to writing functions in Curry-ed form. Curry-ing gives us some flexibility in
partial application of a function, when only the first few arguments are available.
Let us now reason about the correctness of the function myNot. Programs are formal
objects about which we can reason, using equational reasoning — expanding definitions in
either direction and using case analysis.