The best way to get started with OCaml is by using the top-level, as OCaml calls the interactive interpreter (or read-eval-print loop to you Lisp-types). You can evaluate expressions, define and evaluate functions, dynamically load external libraries, trace function execution, etc.
Under Windows I understand there's a simple GUI interface you can use for this, but under any Unix system (and possibly Windows as well?) you should really run the top-level inside of Emacs or Xemacs, via Albert Cohen's stupendous tuareg-mode. It gives you colorized syntax highlighting, pinpointing of any errors that occur, and of course the extensive editing capabilities of Emacs. Here's a screen shot showing a file of OCaml source code in the top window in which I've just evaluated the build function definition with a single keystroke, showing an error message in the interactive top-level in the lower window.
There are also various third-party IDE's which I haven't tried, such as Cameleon.
If you won't use Emacs, you can run the top-level directly, but since there's no command-line editing this will be unbelievably tedious (command-line editing for the top-level is available as a third-party extension). Just run ocaml and type expressions and definitions at it. Note that you need to terminate your input with a double semicolon ;; so that the top-level knows you're ready for it to evaluate. (N.B.: these double semicolons are allowed but not required when you write OCaml code in a file.) Here's an example:
$ ocaml Objective Caml version 3.07+2 # 12 + 13;; - : int = 25 # [1;2;3;4;5];; - : int list = [1; 2; 3; 4; 5] # let f n = n + 1;; val f : int -> int = <fun> # f 1;; - : int = 2 # [f 1; f 2; f 3; f 4; f 5];; - : int list = [2; 3; 4; 5; 6] # List.map f [1;2;3;4;5];; - : int list = [2; 3; 4; 5; 6]
Notice how OCaml tells you not just the result of evaluating an expression but the type of the result as well (e.g. int and int list). The types are expressed in OCaml's type sublanguage.
To exit the top-level, just send end-of-file (in Unix, that means to type control-D at the beginning of a line) or give the directive #quit;;.
If you make a mistake, the top-level will give you an error message with a pretty nice indication of where the problem was detected.
# let f n = n + "foo" / 2 ;; This expression has type string but is here used with type int #
(Note the underlining that the top-level does even just in a terminal session.) The error message above is the most common one you will see; it is explained in Error Messages.
Not all libraries are preloaded in the top-level: only the Core Library and the Standard Library are. The other nine officially-supported libraries that come with the distribution, and any third-party libraries you might be using, must be dynamically loaded into the interpreter. This is very easy to do; suppose you want to use the Unix module; just use the top-level's #load directive to load it:
# #load "unix.cma";; #
File extensions like .cma are explained in Compiling and Running Programs; for now, you can assume that if you want to load a module named Foo (modules all have capitalized names), the command to load it is #load "foo.cma" (note lowercase filename!).
If you find yourself constantly loading the same modules into the top-level over and over again, you can very easily make yourself a custom top-level with those modules preloaded. With most languages I've used, this isn't so easy to do and usually requires access to the source tree for the language distribution; OCaml gives you a program, ocamlmktop, that's specially designed to build custom top-levels.
Note that dynamic loading is not supported on all platforms; sadly, NetBSD is one such. In this case you'll need to build a custom top-level to interactively use certain modules. This command will build a top-level containing all the modules of the Standard Library that need to be dynamically linked:
$ ocamlmktop -thread -o megacaml unix.cma nums.cma str.cma graphics.cma dbm.cma threads.cma -I +labltk labltk.cma bigarray.cma $ ls -l megacaml /usr/local/bin/ocaml -rwxr-xr-x 1 root wheel 986808 Mar 1 21:01 /usr/local/bin/ocaml -rwxrwxr-x 1 keith keith 1468658 May 14 12:48 megacaml $
This is probably overkill, but you can see it's not all that much bigger than the default top-level.
One of the most useful facilities in the top-level is the #trace directive for tracing functions, which shows you the function's arguments and results as it runs. Here's the usual definition of factorial and a trace of fac 6:
# let rec fac n = if n = 1 then n else fac (n-1) * n ;; val fac : int -> int = <fun> # fac 6;; - : int = 720 # #trace fac;; fac is now traced. # fac 6;; fac <-- 6 fac <-- 5 fac <-- 4 fac <-- 3 fac <-- 2 fac <-- 1 fac --> 1 fac --> 2 fac --> 6 fac --> 24 fac --> 120 fac --> 720 - : int = 720 # #untrace fac;; fac is no longer traced. #