Previous Contents Next

Other Libraries in the Distribution

The other libraries provided with the Objective CAML language distribution relate to the following extensions: We will describe the big integer and dynamic loading libraries by using them.

Exact Math

The big numbers library provides exact math functions using integers and rational numbers. Values of type int and float have two limitations: calculations on integers are done modulo the greatest positive integer, which can cause unperceived overflow errors; the results of floating point calculations are rounded, which by propagation can lead to errors. The library presented here mitigates these defects.

This library is written partly in C. For this reason, you have to build an interactive loop that includes this code using the command:
ocamlmktop -custom -o top nums.cma -cclib -lnums
The library contains many modules. The two most important ones are Num for all the operations and Arith_status for controlling calculation options. The general type num is a variant type gathering three basic types:

type num = Int of int
| Big_int of big_int
| Ratio of ratio
The types big_int and ratio are abstract.

The operations on values of type num are followed by the symbol /. For example the addition of two num variables is written +/ and will be of type num -> num -> num. It will be the same for comparisons. Here is the first example that calculates the factorial:

# let rec fact_num n =
if Num.(<=/) n (Num.Int 0) then (Num.Int 1)
else Num.( */ ) n (fact_num ( Num.(-/) n (Num.Int 1)));;
val fact_num : Num.num -> Num.num = <fun>
# let r = fact_num (Num.Int 100);;
val r : Num.num = Num.Big_int <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "93326215443944152681699238856266700490715968264381..."


Opening the Num module makes the code of fact_num easier to read:

# open Num ;;
# let rec fact_num n =
if n <=/ (Int 0) then (Int 1)
else n */ (fact_num ( n -/ (Int 1))) ;;
val fact_num : Num.num -> Num.num = <fun>


Calculations using rational numbers are also exact. If we want to calculate the number e by following the following definition:
e = lim
 
m ->



1 +
1

m



m



 
We should write a function that calculates this limit up to a certain m.

# let calc_e m =
let a = Num.(+/) (Num.Int 1) ( Num.(//) (Num.Int 1) m) in
Num.( **/ ) a m;;
val calc_e : Num.num -> Num.num = <fun>
# let r = calc_e (Num.Int 100);;
val r : Num.num = Ratio <abstr>
# let n = Num.string_of_num r in (String.sub n 0 50) ^ "..." ;;
- : string = "27048138294215260932671947108075308336779383827810..."


The Arith_status module allows us to control some calculations such as the normalization of rational numbers, approximation for printing, and processing null denominators. The arith_status function prints the state of these indicators.

# Arith_status.arith_status();;

Normalization during computation --> OFF
(returned by get_normalize_ratio ())
(modifiable with set_normalize_ratio <your choice>)

Normalization when printing --> ON
(returned by get_normalize_ratio_when_printing ())
(modifiable with set_normalize_ratio_when_printing <your choice>)

Floating point approximation when printing rational numbers --> OFF
(returned by get_approx_printing ())
(modifiable with set_approx_printing <your choice>)

Error when a rational denominator is null --> ON
(returned by get_error_when_null_denominator ())
(modifiable with set_error_when_null_denominator <your choice>)
- : unit = ()


They can be modified according to the needs of a calculation. For example, if we want to print an approximate value for a rational number, we can obtain, for the preceding calculation:

# Arith_status.set_approx_printing true;;
- : unit = ()
# Num.string_of_num (calc_e (Num.Int 100));;
- : string = "0.270481382942e1"


Calculations with big numbers take longer than those with integers and the values occupy more memory. Nevertheless, this library tries to use the most economical representations whenever possible. In any event, the ability to avoid the propagation of rounding errors and to do calculations on big numbers justifies the loss of efficiency.

Dynamic Loading of Code

The Dynlink module offers the ability to dynamically load programs in the form of bytecode. The dynamic loading of code provides the following advantages: The interactive loop of Objective CAML already uses such a mechanism. It is convenient to let the programmer have access to it as well.

During the loading of an object file (with the .cmo extension), the various expressions are evaluated. The main program, that initiated the dynamic loading of the code does not have access to the names of declarations. Therefore it is up to the dynamically loaded module to update a table of functions used by the main program.

Warning


The dynamic loading of code only works for object files in bytecode.


Description of the Module

For dynamic loading of a bytecode file f.cmo, we need to know the access path to the file and the names of the modules that it uses. By default, dynamically loaded bytecode files do not have access to the paths and modules of the libraries in the distribution. Thus we have to add the path and the name of the required modules to the dynamic loading of the module.

init : unit -> unit
    initialize dynamic loading
add_interfaces : string list -> string list -> unit
    add the names of modules and paths for loading
loadfile : string -> unit
    load a bytecode file
clear_avalaible_units : unit -> unit
    empty the names of loadable modules and paths
add_avalaible_units : (string * Digest.t) list -> unit
    add the name of a module and a checksum for loading without needing the interface file
allow_unsafe_modules : bool -> unit
    allow the loading of files containing external declarations
loadfile_private : string -> unit
    the loaded module is not accessible to modules loaded later
The checksum of an interface .cmi can be obtained from the extract_crc command found in the catalog of libraries in the distribution.

Figure 8.10: Functions of the Dynlink module.


Many errors can occur during a request to load a module. Not only must the file exist with the right interface in one of the paths, but the bytecode must also be correct and loadable. These errors are gathered in the type error used as an argument to the Error exception and to the error function of type error -> string that allows the conversion of an error into a clear description.

Example

To write a small program that allows us to illustrate dynamic loading of bytecode, we provide three modules: The F module is defined in the file f.ml:

let g () =
print_string "I am the 'f' function by default\n" ; flush stdout ;;
let f = ref g ;;


The Mod1 module is defined in the file mod1.ml:

print_string "The 'Mod1' module modifies the value of 'F.f'\n" ; flush stdout ;;
let g () =
print_string "I am the 'f' function of module 'Mod1'\n" ;
flush stdout ;;
F.f := g ;;


The Mod2 module is defined in the file mod2.ml:

print_string "The 'Mod2' module modifies the value of 'F.f'\n" ; flush stdout ;;
let g () =
print_string "I am the 'f' function of module 'Mod2'\n" ;
flush stdout ;;
F.f := g ;;


Finally we define in the file main.ml, a main program that calls the original function referenced by F.f, loads the Mod1 module, calls F.f again, then loads the Mod2 module and calls the F.f function one last time:

let main () =
try
Dynlink.init () ;
Dynlink.add_interfaces [ "Pervasives"; "F" ; "Mod1" ; "Mod2" ]
[ Sys.getcwd() ; "/usr/local/lib/ocaml/" ] ;
!(F.f) () ;
Dynlink.loadfile "mod1.cmo" ; !(F.f) () ;
Dynlink.loadfile "mod2.cmo" ; !(F.f) ()
with
Dynlink.Error e -> print_endline (Dynlink.error_message e) ; exit 1 ;;

main () ;;
The main program must, in addition to initializing the dynamic loading, declare by a call to Dynlink.add_interfaces the interface used.

We compile all of these modules:
$ ocamlc -c f.ml
$ ocamlc -o main dynlink.cma f.cmo main.ml
$ ocamlc -c f.cmo mod1.ml
$ ocamlc -c f.cmo mod2.ml
If we execute program main, we obtain:
$ main
I am the 'f' function by default
The 'Mod1' module modifies the value of 'F.f'
I am the 'f' function of module 'Mod1'
The 'Mod2' module modifies the value of 'F.f'
I am the 'f' function of module 'Mod2'
Upon the dynamic loading of a module, its code is executed. This is demonstrated in our example, with the outputs beginning with The 'Mod.... The possible side effects that it contains are therefore reflected at the level of the program that caused the code to be loaded. This is why the different calls to F.f call different functions.

The Dynlink library offers the basic mechanism for dynamically loading bytecode. The programmer still has to manage tables such that the loading will really be effective.






Previous Contents Next