Computer Science Canada

[O'Caml-tut] G'day O'Caml

Author:  wtd [ Mon Jul 19, 2004 8:34 pm ]
Post subject:  [O'Caml-tut] G'day O'Caml

Getting O'Caml

First off, O'Caml 3.08 is available for download here. For Linux users there's source code available, and for Windows and Mac OS X users precompiled binaries are also available. The ocaml program both runs compiled O'Caml programs and offers an interactive interpreter for quick experimentation. The normal prompt when using this interpreter is simply "#". The ocamlc program compiles O'Caml source code to code which can be executed by ocaml.

A Quick Introduction

Objective-Caml is a strongly typed language. However, the vast majority of the time, it is able to infer what types are being used, so there's no need to specify them yourself. This alone greatly speeds development.

O'Caml is a functional language. Functions exist in O'Caml as first class things which can be passed around as easily as ints or chars in certain other languages which shall remain nameless.

O'Caml is also an object-oriented language, supporting multiple inheritance. Though the syntax is a bit different than what you might be accustomed to, O'Caml offers great things for fans of object-oriented development.

First Program

code:
$ ocaml
        Objective Caml version 3.07+2
 
# print_endline "G'day O'Caml!";;
G'day O'Caml!
- : unit = ()
#


Obviously the code I ran simply printed the string "G'day O'Caml", tacking on a newline. So what's this about?

code:
- : unit = ()


Everything in O'Caml has a return value. The "-" indicates that the return value of the functiion wasn't bound to any name. Normally if we had bound it to a name, that name would appear here instead. "unit" indicates the type of the return value. This is somewhat analogous to "void" in C-derived languages. "()" is always the value of type "unit", but this is where the actual value returned would be indicated.

Binding Names to Values

Let's look at a slightly more complex example to demonstrate this. Here I'll alo be demonstrating the "let" syntax used to bind names to values. This shouldn't be confused with the concept of variables in other languages. I'll simply be giving a name to a particular value. That name can then stand in wherever I might otherwise have manually typed the value, but it can't be modified, except by rebinding that name to a different value.

code:
# let greeting = "G'day O'Caml!";;
val greeting : string = "G'day O'Caml!"
# print_endline greeting;;
G'day O'Caml!
- : unit = ()
#


First, let me note that ";;" in much the same as ";" in C, C++, Java, etc. In larger O'Caml programs it rarely proves necessary due to the nature of O'Caml syntax, but it's essential here to tell the interactive interpreter we're done typing and want to see the result of what we've input.

The above code should otherwise be pretty self-explanatory.

Local Binding

There's a twist though. In the above example, "greeting" is now visible to the rest of the program. It will also rebind "greeting" from whatever it was bound to before.

code:
# let greeting = "Hello, Chris";;
val greeting : string = "Hello, Chris"
# let greeting = "G'day O'Caml!";;
val greeting : string = "G'day O'Caml!"
# print_endline greeting;;
G'day O'Caml!
- : unit = ()
# greeting;;
- : string = "G'day O'Caml!"
#


Instead, howerver, we could use "let ... in ..." to bind a value to "greeting" only for the duration of a single expression.

code:
# let greeting = "Hello, Chris";;
val greeting : string = "Hello, Chris"
# let greeting = "G'day O'Caml!" in
        print_endline greeting;;
G'day O'Caml!
- : unit = ()
# greeting;;
- : string = "Hello, Chris"
#


The ; Operator

Of course, this isn't terribly valuable. If we could locally bind a value for a sequence of expressions, that might be valuable.

code:
# let greeting = "G'day O'Caml!" in
        print_endline greeting;
        print_endline ("Did you say \"" ^ greeting ^ "\"?");;
G'day O'Caml!
Did you say "G'day O'Caml!"?
- : unit = ()
#


Here the ";" creates a sequence of expressions. A similar syntax is used for creating sequences of data, such as lists which are approximately the equivalent of arrays.

The "^" operator concatenates strings.

Of course, it should also be noted that the above can be improved by using more than one binding.

code:
# let greeting = "G'day O'Caml!" in
        let retort = "Did you say \"" ^ greeting ^ "\"?" in
                print_endline greeting;
                print_endline retort;;
G'day O'Caml!
Did you say "G'day O'Caml!"?
- : unit = ()
#


Functions

Of course, being a functional language, functions are integral to O'Caml. Let's create a function to allow for greeting anyone.

code:
# let greet name =
        let greeting = "Hello, " ^ name in
                print_endline greeting;;
val greet : string -> unit = <fun>
#


Callling this function is then as simple as:

code:
# greet "O'Caml";;
Hello, O'Caml
- : unit = ()
#


Where the ability to pass functions to other functions as arguments is easily demonstrated by applying a function to a list of items. For instance, I may want to greet a list of names.

code:
# let people = ["Chris"; "Bob"; "Joe"] in
        List.iter greet people;;
Hello, Chris
Hello, Bob
Hello, Joe
- : unit = ()
#


List.iter is a function in the List module. That module is present by default in the O'Caml interactive interpreter, but programs should include the following line to have access to that module. The same syntax is used for accessing other modules as well.

code:
open List


This function allows us to apply functions which return "unit". To apply functions which return another value, we should use the List.map functions. For instance, we might want to add 1 to each number in a long list of numbers.

code:
# let new_numbers = List.map ((+) 1) [1;2;3];;
val new_numbers : int list = [2; 3; 4]
#


Operators as Functions

Operators such as "+" are simply functions which take two arguments.

code:
(+) 1 2


Is equivalent to:

code:
1 + 2


Partial Application of Functions

In O'Caml a function lacking one or more arguments simply returns a function which takes those arguments. Thus we can partially apply the (+) function.

code:
# let add_one = (+) 1 in
        add_one 42;;
- : int = 43
#


Recursion and Pattern Matching as an Alternative to List.iter

One of the most common ways of acting on lists in functional languages is recursion. Do something on one element of a list, then call the same function with the remaining elements. When you get down to an empty list, return an empty list.

This algorithm is easily implemented in O'Caml. The "rec" keyword is needed for any function which is recursive.

code:
let rec greet_people people =
        match people with
                [] -> []
                | first::others -> greet first; greet_people others


Since we're only taking a single argument, and immediately using match on it (we never actually refer to "people" again), we can simplify this to:

code:
let rec greet_people = function
        [] -> []
        | first::others -> greet first; greet_people others


: