A complete OCaml application (a one-file application) typically consists of a sequence of let definitions terminated by the invocation of a "main" function. Let's construct a complete application — a version of the Unix cat command — around the cat function from some pages back.
let cat filename = let chan = open_in filename in let size = 4 * 1024 in let buffer = String.create size in let eof = ref false in while not !eof do let len = input chan buffer 0 size in if len > 0 then print_string (String.sub buffer 0 len) else eof := true done let main () = let len = (Array.length Sys.argv) in let argv = (Array.sub Sys.argv 1 (len-1)) in (* skip argv0 *) Array.iter cat argv let _ = main ()
The main function interfaces cat to the outside world, and emulates the Unix cat command which takes several filenames on the command line and concatenates their contents on standard output.
Unix processes access the command line through a vector (provided by the OS) known as argv (the argument vector). OCaml makes this available as an array in the Sys module called argv. Unix stores the name of the running process in the zeroth element of argv, so Sys.argv.(0) will be something like "cat". We need to ignore that (or we'd end up catting our own executable code!) so we use Array.sub to return the subarray of Sys.argv from element 1 (the second element) to the end. Finally, we implement the Unix cat command by using Array.iter to apply the cat function to each filename on the command line.
The very last line of the program actually invokes the main function by applying it to () (since the domain of main is unit). It's conventional in Ocaml to bind the return value of main (which we don't care about) to _ by way of ignoring it. This is purely conventional (as is the use of a function called main) and is really a way of avoiding a ;;. I recommend you always structure your application this way if you don't want to learn this little detail of the syntax.
Let's look at a simple CGI application that displays the IP address and hostname of the machine that makes the request. This program handles the CGI protocol itself (it doesn't even take any parameters, so this is easy):
(* report IP address and hostname of machine requesting this web page *) (* format addr and host into HTML template*) let html (addr, host) = Printf.printf "\ Content-Type: text/html\n\ \n\ <title>IP address and hostname</title>\n\ <h1>IP address and hostname</h1>\n\ <p>Your IP address is %s</p>\n\ <p>Your hostname is %s</p>\n\ " addr host (* return pair of (ipaddr, hostname) strings; use CGI variable * REMOTE_ADDR for IP address and convert that to hostname if * possible *) let addrhost () = let addr = Sys.getenv "REMOTE_ADDR" in addr, (Unix.gethostbyaddr (Unix.inet_addr_of_string addr)).Unix.h_name let main () = html (addrhost ()) let _ = main ()
The only thing worth mentioning about this program is that it uses functions from the Unix module to do the DNS lookup, so that it doesn't have to exec an external program.
I compiled this program using ocamake with this command: ocamake -o address address.ml unix.cma. You can try it out here.
More complex web applications might use the Cgi module, which is sufficient for simple applications (providing GET and POST handling and URL decoding) or ocamlnet, which provides a sophisticated O-O interface; here's a trivial example of the latter:
let actobj = new Netcgi.std_activation() in actobj # set_header(); actobj # output # output_string "<HTML><BODY>Hello world!</BODY></HTML>\n"; actobj # output # commit_work()