(* When an OCaml source file is compiled, it becomes a module. The name of the module is the capitalized form of the filename. For example, if the source file is "my_module.ml", the module name is "My_module". Modules can also be created explicitly within a source file. If "my_module.ml" contains "module Foo = struct ... end", a module named "My_module.Foo" will be created. Here is an example of the definition and use of two modules within a single source file: *) module Alpha = struct let name = "first" end module Omega = struct let name = "last" end let () = Printf.printf "Alpha is %s, Omega is %s.\n" Alpha.name Omega.name (* Alpha is first, Omega is last. *) (*-----------------------------*) (* The "#use" and "#load" commands are known as toplevel directives. They can only be used while interacting with the interpreter or from scripts that are run using the "ocaml" program. *) (* "#use" loads a source file into the current scope. *) #use "FileHandle.ml";; (* "#load" loads a module from a compiled bytecode file. This has the same effect as including this file during bytecode compilation. *) #load "FileHandle.cmo";; (* "#load" can be used with libraries as well as modules. Bytecode libraries use an extension of ".cma". *) #load "library.cma";; (* The "open" statement can be used in any source file. It allows any values defined within a module to be used without being prefixed by the module name. *) open FileHandle (* Modules form a hierarchy; submodules can be opened in a similar fashion by prefixing them with the parent module's name. *) open Cards.Poker (* It is often convenient to use Gerd Stolpmann's "findlib" system, which makes it considerably easier to load libraries into the interpreter. *) # #use "topfind";; - : unit = () Findlib has been successfully loaded. Additional directives: #require "package";; to load a package #list;; to list the available packages #camlp4o;; to load camlp4 (standard syntax) #camlp4r;; to load camlp4 (revised syntax) #predicates "p,q,...";; to set these predicates Topfind.reset();; to force that packages will be reloaded #thread;; to enable threads - : unit = () # #require "extlib";; /usr/lib/ocaml/3.10.2/extlib: added to search path /usr/lib/ocaml/3.10.2/extlib/extLib.cma: loaded (* The above use of "#require" has the same effect as typing the following: *) #directory "+extlib";; #load "extLib.cma";; (* More information on the "findlib" system is available here: http://projects.camlcity.org/projects/findlib.html The "#directory" directive above is built into OCaml and allows you to add additional directories to the path that is searched when loading modules. You can use a prefix of '+' to indicate that the directory is under the standard library path, which is usually something like "/usr/lib/ocaml/3.10.2/". Modules can be easily aliased using assignment. This will also cause the interpreter to output the module's signature, which can be used as a quick reference. *) # module S = ExtString.String;; module S : sig val init : int -> (int -> char) -> string val find : string -> string -> int val split : string -> string -> string * string val nsplit : string -> string -> string list val join : string -> string list -> string ... end # S.join;; - : string -> string list -> string = <fun> (* Many useful libraries can be found at The Caml Hump: http://caml.inria.fr/cgi-bin/hump.cgi *) |
(* Interfaces, also known as module types or signatures, are usually saved in files with the same name as the corresponding module but with a ".mli" extension. For instance, if the module is defined in "YourModule.ml", the interface will be in "YourModule.mli". *) (* YourModule.mli *) val version : string (* YourModule.ml *) let version = "1.00" (* As with modules, interfaces can also be defined explicitly inside of a source file. *) module type YourModuleSignature = sig val version : string end module YourModule : YourModuleSignature = struct let version = "1.00" end (* Signatures can also be anonymous. *) module YourModule : sig val version : string end = struct let version = "1.00" end |
(* Due to static typing, missing modules are detected at compilation time, so this is not normally an error you can catch (or need to). When using ocaml interactively or as an interpreter, the "#load" directive can fail, resulting in a message like the following: Cannot find file <filename>. being printed to standard output. This is also not an error you can catch, and its occurrence will not stop the script from executing. It is possible to dynamically load modules and detect the failure of this operation with Dynlink. An example is given in the next recipe. *) |
(* Registry.ml *) let (registry : (string, unit -> unit) Hashtbl.t) = Hashtbl.create 32 (* SomeModule.ml *) let say_hello () = print_endline "Hello, world!" let () = Hashtbl.replace Registry.registry "say_hello" say_hello (* Main program *) let filename = "SomeModule.cmo" let funcname = "say_hello" let () = Dynlink.init (); (try Dynlink.loadfile filename with Dynlink.Error e -> failwith (Dynlink.error_message e)); (Hashtbl.find Registry.registry funcname) () (* Note that the Dynlink module currently supports dynamic loading of bytecode modules only. There is a project to add support for dynamic loading of native code which has been merged with OCaml's CVS HEAD. Details are available at http://alain.frisch.fr/natdynlink.html *) |
#load "str.cma";; module Flipper : sig val flip_boundary : string -> string val flip_words : string -> string end = struct let separatrix = ref " " (* hidden by signature *) let flip_boundary sep = let prev_sep = !separatrix in separatrix := sep; prev_sep let flip_words line = let words = Str.split (Str.regexp_string !separatrix) line in String.concat !separatrix (List.rev words) end |
(* This is very difficult to do in OCaml due to the lack of reflection capabilities. Determining the current module name is reasonably easy, however, by using the __FILE__ constant exposed by camlp4's macro extensions. *) (*pp camlp4of *) let __MODULE__ = String.capitalize (Filename.chop_extension __FILE__) let () = Printf.printf "I am in module %s\n" __MODULE__ |
(* Use the built-in function, "at_exit", to schedule clean-up handlers to run when the main program exits. *) #load "unix.cma";; let logfile = "/tmp/mylog" let lf = open_out logfile let logmsg msg = Printf.fprintf lf "%s %d: %s\n%!" Sys.argv.(0) (Unix.getpid ()) msg (* Setup code. *) let () = logmsg "startup" (* Clean-up code. *) let () = at_exit (fun () -> logmsg "shutdown"; close_out lf) |
(* To add a directory to the module include path, pass the "-I" option to any of the compiler tools. For example, if you have a module in ~/ocamllib called Utils with a filename of utils.cmo, you can build against this module with the following: *) $ ocamlc -I ~/ocamllib utils.cmo test.ml -o test (* Within the toplevel interpreter, and from ocaml scripts, you can use the "#directory" directive to add directories to the include path: *) #directory "/home/myuser/ocamllib";; #load "utils.cmo";; (* In both cases, prefixing the include directory with a '+' indicates that the directory should be found relative to the standard include path. *) #directory "+pcre";; #load "pcre.cma";; (* If you have findlib installed, you can print out the include path by typing "ocamlfind printconf path" at the command line. *) $ ocamlfind printconf path /usr/local/lib/ocaml/3.10.2 /usr/lib/ocaml/3.10.2 /usr/lib/ocaml/3.10.2/METAS (* Instead of keeping a directory of ".cmo" (or ".cmx") files, you may prefer to build a library (".cma" for bytecode, ".cmxa" for native). This will pack all of your modules into a single file that is easy to use during compilation: *) $ ocamlc -a slicer.cmo dicer.cmo -o tools.cma $ ocamlc tools.cma myprog.ml -o myprog |
(* The easiest way to prepare a library for distribution is to build with OCamlMakefile and include a META file for use with findlib. OCamlMakefile is available here: http://www.ocaml.info/home/ocaml_sources.html#OCamlMakefile findlib is available here: http://projects.camlcity.org/projects/findlib.html *) (* Put the following in a file called "Makefile" and edit to taste: *) OCAMLMAKEFILE = OCamlMakefile RESULT = mylibrary SOURCES = mylibrary.mli mylibrary.ml PACKS = pcre all: native-code-library byte-code-library install: libinstall uninstall: libuninstall include $(OCAMLMAKEFILE) (* Put the following in a file called "META" and edit to taste: *) name = "mylibrary" version = "1.0.0" description = "My library" requires = "pcre" archive(byte) = "mylibrary.cma" archive(native) = "mylibrary.cmxa" (* Now you can build bytecode and native libraries with "make" and install them into the standard library location with "make install". If you make a change, you will have to "make uninstall" before you can "make install" again. Once a library is installed, it's simple to use: *) $ ledit ocaml Objective Caml version 3.10.2 # #use "topfind";; - : unit = () Findlib has been successfully loaded. Additional directives: #require "package";; to load a package #list;; to list the available packages #camlp4o;; to load camlp4 (standard syntax) #camlp4r;; to load camlp4 (revised syntax) #predicates "p,q,...";; to set these predicates Topfind.reset();; to force that packages will be reloaded #thread;; to enable threads - : unit = () # #require "mylibrary";; /usr/lib/ocaml/3.10.2/pcre: added to search path /usr/lib/ocaml/3.10.2/pcre/pcre.cma: loaded /usr/local/lib/ocaml/3.10.2/mylibrary: added to search path /usr/local/lib/ocaml/3.10.2/mylibrary/mylibrary.cma: loaded (* To compile against your new library, use the "ocamlfind" tool as a front-end to "ocamlc" and "ocamlopt": *) $ ocamlfind ocamlc -package mylibrary myprogram.ml -o myprogram $ ocamlfind ocamlopt -package mylibrary myprogram.ml -o myprogram |
(* OCaml supports native compilation. If module load time is an issue, it's hard to find a better solution than "ocamlopt". If compilation is slow as well, try "ocamlopt.opt", which is the natively-compiled native compiler. *) |
(* This recipe is not relevant or applicable to OCaml. *) |
#load "unix.cma";; (* The Unix module returns the time as a float. Using a local module definition and an "include", we can override this function to return an int32 instead. (This is a bit silly, but it illustrates the basic technique. *) module Unix = struct include Unix let time () = Int32.of_float (time ()) end (* Use the locally modified Unix.time function. *) let () = let start = Unix.time () in while true do Printf.printf "%ld\n" (Int32.sub (Unix.time ()) start) done (* Operators can also be locally modified. Here, we'll temporarily define '-' as int32 subtraction. *) let () = let ( - ) = Int32.sub in let start = Unix.time () in while true do Printf.printf "%ld\n" (Unix.time () - start) done |
(* There are two built-in functions that raise standard exceptions. Many standard library functions use these. "invalid_arg" raises an Invalid_argument exception, which takes a string parameter: *) let even_only n = if n land 1 <> 0 (* one way to test *) then invalid_arg (string_of_int n); (* ... *) () (* "failwith" raises a Failure exception, which also takes a string parameter (though it is typically used to identify the name of the function as opposed to the argument). *) let even_only n = if n mod 2 <> 0 (* here's another *) then failwith "even_only"; (* ... *) () (* In most cases, it is preferable to define your own exceptions. *) exception Not_even of int let even_only n = if n land 1 <> 0 then raise (Not_even n); (* ... *) () (* OCaml does not provide a facility for emitting warnings. You can write to stderr, which may be an acceptable substitute. *) let even_only n = let n = if n land 1 <> 0 (* test whether odd number *) then (Printf.eprintf "%d is not even, continuing\n%!" n; n + 1) else n in (* ... *) () |
(* Generally, it is best to use tables of functions, possibly with Dynlink, to delay the choice of module and function until runtime. It is however possible--though inelegant--to (ab)use the toplevel for this purpose. *) open Printf (* Toplevel evaluator. Not type-safe. *) let () = Toploop.initialize_toplevel_env () let eval text = let lexbuf = (Lexing.from_string text) in let phrase = !Toploop.parse_toplevel_phrase lexbuf in ignore (Toploop.execute_phrase false Format.std_formatter phrase) let get name = Obj.obj (Toploop.getvalue name) let set name value = Toploop.setvalue name (Obj.repr value) (* Some module and value names, presumably not known until runtime. *) let modname = "Sys" let varname = "ocaml_version" let aryname = "argv" let funcname = "getenv" (* Use the toplevel to evaluate module lookups dynamically. *) let () = eval (sprintf "let (value : string) = %s.%s;;" modname varname); print_endline (get "value"); eval (sprintf "let (values : string array) = %s.%s;;" modname aryname); Array.iter print_endline (get "values"); eval (sprintf "let (func : string -> string) = %s.%s;;" modname funcname); print_endline ((get "func") "HOME"); |
(* There are several tools for translating C header files to OCaml bindings, many of which can be found at The Caml Hump: http://caml.inria.fr/cgi-bin/hump.en.cgi?sort=0&browse=42 Of the available tools, "ocamlffi" (also known as simply "FFI") seems to work best at accomplishing the task of parsing header files, but it has not been maintained in many years and cannot handle the deep use of preprocessor macros in today's Unix headers. As a result, it is often necessary to create a header file by hand, and so long as this is required, better results can be achieved with Xavier Leroy's CamlIDL tool. CamlIDL can be found here: http://caml.inria.fr/pub/old_caml_site/camlidl/ The following recipes will use CamlIDL. First, we'll wrap the Unix "gettimeofday" system call by writing the following to a file named "time.idl": *) quote(C,"#include <sys/time.h>"); struct timeval { [int32] int tv_sec; [int32] int tv_usec; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; int gettimeofday([out] struct timeval *tv, [in] struct timezone *tz); (* We can now build three files, "time.ml", "time.mli", and "time_stubs.c", corresponding to the OCaml implementation, OCaml interface, and OCaml-to-C stubs, by running the following command: *) $ camlidl -no-include time.idl (* CamlIDL automatically translates the two structs defined in the IDL into OCaml record types and builds an external function reference for "gettimeofday", resulting in the following generated OCaml implementation in "time.ml": *) (* File generated from time.idl *) type timeval = { tv_sec: int32; tv_usec: int32; } and timezone = { tz_minuteswest: int; tz_dsttime: int; } external gettimeofday : timezone option -> int * timeval = "camlidl_time_gettimeofday" (* Now, we can use "ocamlc -c" as a front-end to the C compiler to build the stubs, producing time_stubs.o. *) $ ocamlc -c time_stubs.c (* The OCaml source can be built and packed into a library along with the compiled stubs using "ocamlc -a": *) $ ocamlc -a -custom -o time.cma time.mli time.ml time_stubs.o \ -cclib -lcamlidl (* Finally, we can write a simple test program to use our newly-exposed "gettimeofday" function. *) (* test.ml *) let time () = let res, {Time.tv_sec=seconds; tv_usec=microseconds} = Time.gettimeofday None in Int32.to_float seconds +. (Int32.to_float microseconds /. 1_000_000.) let () = Printf.printf "%f\n" (time ()) (* Compiling this test program is straightforward. *) $ ocamlc -o test time.cma test.ml (* Running it produces the current time with millisecond precision. *) $ ./test 1217173408.931277 (*---------------------------*) (* The next two recipes will wrap the Unix "ioctl" function, allowing us to make a few low-level I/O system calls. To make things easier, we'll use the following Makefile (make sure you use tabs, not spaces, if you cut and paste this code): *) all: jam winsz jam: ioctl.cma jam.ml ocamlc -o jam ioctl.cma jam.ml winsz: ioctl.cma winsz.ml ocamlc -o winsz ioctl.cma winsz.ml ioctl.cma: ioctl.mli ioctl.ml ioctl_stubs.o ocamlc -a -custom -o ioctl.cma ioctl.mli ioctl.ml ioctl_stubs.o \ -cclib -lcamlidl ioctl_stubs.o: ioctl_stubs.c ocamlc -c ioctl_stubs.c ioctl.mli ioctl.ml ioctl_stubs.c: ioctl.idl camlidl -no-include ioctl.idl clean: rm -f *.cma *.cmi *.cmo *.c *.o ioctl.ml ioctl.mli jam winsz (*---------------------------*) (* ioctl.idl: *) quote(C,"#include <sys/ioctl.h>"); enum ioctl { TIOCSTI, TIOCGWINSZ, }; int ioctl([in] int fd, [in] enum ioctl request, [in, out, string] char *argp); (*---------------------------*) (* jam - stuff characters down STDIN's throat *) (* Simulate input on a given terminal. *) let jam ?(tty=0) s = String.iter (fun c -> ignore (Ioctl.ioctl tty Ioctl.TIOCSTI (String.make 1 c))) s (* Stuff command-line arguments into STDIN. *) let () = jam (String.concat " " (List.tl (Array.to_list (Sys.argv)))) (*---------------------------*) (* winsz - find x and y for chars and pixels *) (* Decode a little-endian short integer from a string and offset. *) let decode_short s i = Char.code s.[i] lor Char.code s.[i + 1] lsl 8 (* Read and display the window size. *) let () = let winsize = String.make 8 '\000' in ignore (Ioctl.ioctl 0 Ioctl.TIOCGWINSZ winsize); let row = decode_short winsize 0 in let col = decode_short winsize 2 in let xpixel = decode_short winsize 4 in let ypixel = decode_short winsize 6 in Printf.printf "(row,col) = (%d,%d)" row col; if xpixel <> 0 || ypixel <> 0 then Printf.printf " (xpixel,ypixel) = (%d,%d)" xpixel ypixel; print_newline () |
(* Building libraries with C code is much easier with the aid of OCamlMakefile. The following Makefile is all it takes to build the "time" library from the previous recipe: *) OCAMLMAKEFILE = OCamlMakefile RESULT = time SOURCES = time.idl NOIDLHEADER = yes all: byte-code-library native-code-library include $(OCAMLMAKEFILE) (* Now, a simple "make" will perform the code generation with camlidl and produce static and dynamic libraries for bytecode and native compilation. Furthermore, "make top" will build a custom toplevel interpreter called "time.top" with the Time module built in: *) $ ./time.top Objective Caml version 3.10.2 # Time.gettimeofday None;; - : int * Time.timeval = (0, {Time.tv_sec = 1217483550l; Time.tv_usec = 645204l}) (* With the addition of a "META" file combined with the "libinstall" and "libuninstall" targets, this library can be installed to the standard location for use in other projects. See recipe 12.8, "Preparing a Module for Distribution", for an example. *) |
(** Documentation for OCaml programs can be generated with the ocamldoc tool, included in the standard distribution. Special comments like this one begin with two asterisks which triggers ocamldoc to include them in the documentation. The first special comment in a module becomes the main description for that module. *) (** Comments can be placed before variables... *) val version : string (** ...functions... *) val cons : 'a -> 'a list -> 'a list (** ...types... *) type choice = Yes | No | Maybe of string (* ... and other constructs like classes, class types, modules, and module types. Simple comments like this one are ignored. *) (** {2 Level-two headings look like this} *) (** Text in [square brackets] will be formatted using a monospace font, ideal for identifiers and other bits of code. Text written in curly braces with a bang in front {!Like.this} will be hyperlinked to the corresponding definition. *) (* To generate HTML documentation, use a command like the following: *) $ ocamldoc -html -d destdir Module1.mli Module1.ml ... (* To generate Latex documentation, use a command like the following: *) $ ocamldoc -latex -d destdir Module1.mli Module1.ml ... (* If you use OCamlMakefile, you can type "make doc" to build HTML and PDF documentation for your entire project. You may want to customize the OCAMLDOC and DOC_FILES variables to suit your needs. *) |
(* Installing a module from The Caml Hump differs from project to project, since it is not as standardized as CPAN. However, in most cases, "make" and "make install" do what you expect. Here's how to install easy-format, which can be found on the Hump at the following URL: http://caml.inria.fr/cgi-bin/hump.en.cgi?contrib=651 *) $ tar xzf easy-format.tar.gz $ cd easy-format $ make ocamlc -c easy_format.mli ocamlc -c -dtypes easy_format.ml touch bytecode ocamlc -c easy_format.mli ocamlopt -c -dtypes easy_format.ml touch nativecode $ sudo make install [sudo] password for root: ........ echo "version = \"1.0.0\"" > META; cat META.tpl >> META INSTALL_FILES="META easy_format.cmi easy_format.mli"; \ if test -f bytecode; then \ INSTALL_FILES="$INSTALL_FILES easy_format.cmo "; \ fi; \ if test -f nativecode; then \ INSTALL_FILES="$INSTALL_FILES easy_format.cmx easy_format.o"; \ fi; \ ocamlfind install easy-format $INSTALL_FILES Installed /usr/local/lib/ocaml/3.10.2/easy-format/easy_format.o Installed /usr/local/lib/ocaml/3.10.2/easy-format/easy_format.cmx Installed /usr/local/lib/ocaml/3.10.2/easy-format/easy_format.cmo Installed /usr/local/lib/ocaml/3.10.2/easy-format/easy_format.mli Installed /usr/local/lib/ocaml/3.10.2/easy-format/easy_format.cmi Installed /usr/local/lib/ocaml/3.10.2/easy-format/META |
(* Some.ml *) module Module = struct (* set the version for version checking *) let version = "0.01" (* initialize module globals (accessible as Some.Module.var1 *) let var1 = ref "" let hashit = Hashtbl.create 0 (* file-private lexicals go here *) let priv_var = ref "" let secret_hash = Hashtbl.create 0 (* here's a file-private function *) let priv_func () = (* stuff goes here. *) () (* make all your functions, whether exported or not *) let func1 () = (* ... *) () let func2 () = (* ... *) () let func3 a b = (* ... *) () let func4 h = (* ... *) () (* module clean-up code here *) let () = at_exit (fun () -> (* ... *) ()) end (* Some.mli *) module Module : sig val version : string val var1 : string ref val hashit : (string, string) Hashtbl.t (* priv_var, secret_hash, and priv_func are omitted, making them private and inaccessible... *) val func1 : unit -> unit val func2 : unit -> unit val func3 : 'a -> 'b -> unit val func4 : (string, string) Hashtbl.t -> unit end |
(* Use "findlib". You can use the "ocamlfind" program to get a list of installed libraries from the command line: *) $ ocamlfind list benchmark (version: 0.6) bigarray (version: [distributed with Ocaml]) cairo (version: n/a) cairo.lablgtk2 (version: n/a) calendar (version: 2.0.2) camlimages (version: 2.2.0) camlimages.graphics (version: n/a) camlimages.lablgtk2 (version: n/a) camlp4 (version: [distributed with Ocaml]) camlp4.exceptiontracer (version: [distributed with Ocaml]) camlp4.extend (version: [distributed with Ocaml]) ... (* You can also use the "#list" directive from the interpreter: *) $ ledit ocaml Objective Caml version 3.10.2 # #use "topfind";; - : unit = () Findlib has been successfully loaded. Additional directives: #require "package";; to load a package #list;; to list the available packages #camlp4o;; to load camlp4 (standard syntax) #camlp4r;; to load camlp4 (revised syntax) #predicates "p,q,...";; to set these predicates Topfind.reset();; to force that packages will be reloaded #thread;; to enable threads - : unit = () # #list;; benchmark (version: 0.6) bigarray (version: [distributed with Ocaml]) cairo (version: n/a) cairo.lablgtk2 (version: n/a) ... |