Delimited overloading is a syntax extension to ease the writing of efficient arithmetic expressions in OCaml. This project was sponsored by Jane Street Capital during their OCaml Summer Project 2008. We are very grateful for their support. See the complete proposal (PDF).

This project is hosted on OCamlForge.

Description

This project provides three syntax extensions provided by several modules. The information below should give you a taste of the possibilities of these syntax extensions but the main reference is the ocamldoc help.

pa_do

This module allows to locally overload operators and functions. The syntax is X.(e) where X is a module name and e is an expression. For example, you can write

Big_int.( 23 ** 567 mod 45 + "123456789123456789123456789") Int64.(float(q * of_int n - (s * s) lsr 3)) /. float n Int32.(incr x)

Big_int

Int64

Int32

Big_int

Int32

Int64

Nativeint

Complex

Num

Big_int

Ratio

nums.cma

num

Int

Float

Complex

I

Complex.(log(z + 2I) = I / u)

and this will be transformed into the appropriate, andfunction calls (the string will be checked to represent a validat compile time). The standard OCaml numeric modules(provided by the Pa_do.Std module),and(in a separate extension pa_do_num because they require thelibrary (ocamlfind package) to be loaded in camlp4 for static checking) are overloaded, as well as two "phantom" modulesand. Themodule use the nameto denote the imaginary unit so one can write

You can easily define your own overloadings by either using the concrete syntax or the API. As an example, suppose you have a module X that defines the usual arithmetic functions, add , sub , mul , div and the unary negation neg . To be able to write expressions using the usual arithmetic operators, like X.(x * y + z) , simply put

OVERLOAD_ARITHMETIC X

OVERLOAD_STRING Rope(of_string) OVERLOAD_COMPARISON Rope(compare) OVERLOAD Rope((^) -> concat2) OVERLOAD_STRING_GET Rope(get)

Rope.(if max s u <= "pre" ^ u then s.[0] else u.[0])

Poly

OVERLOAD_INT Poly(constant_int_poly) OVERLOAD_FLOAT Poly(constant_poly) OVERLOAD_ARITHMETIC Poly OVERLOAD Poly((=) -> equal; (<>) -> not_equal; ( ** ) -> pow) OVERLOAD_POLY_VAR Poly(var_of_string)

let p = Poly.(1 + `x * `x + `x * `y + `x**k)

q

x

y

var_of_string "x"

Poly.(`x**2)

2

at the beginning of your source file. As another example, let us use the Rope library. To make it more like strings, one can declareWith this one can just write. Finally, for a polynomial moduleon the field of the reals, one probably would like to use (the name of the functions have been chosen to be self-documenting):Then one can use the natural lookingto declare the polynomialwith variablesand. Note that the library caches the evaluation of constants so there will actually be only a singlefor the entire source file. To support, wherehas not to be transformed into a polynomial, one has to add a new rule with the constants function from the API

Of course, to avoid putting the OVERLOAD declarations in every source file, one would usually prefer to write a syntax extension module and perform these tasks with the Delimited overloading API.

Macro

Pa_do.Macro enables macros compatible with delimited overloading—and with other syntax extensions by means of a clear API. They provide a way write ad-hoc polymorphic code. They also correct some design flaws of the original macros (especially the interaction of IFDEF and DEFINE ).

As a simple example of what macros enable you to do, here is a generic "incrementor":

DEFINE INCR(M,x) = M.(x := !x + 1)

INCR(Int, x)

INCR(Float, x)

+

*

DEFINE RK4(M) = let onesixth = 1. /. 6. and onethird = 1. /. 3. in fun ?(n=200) f t0 t1 x0 -> let h = (t1 -. t0) /. float n in let xt = Array.make (n + 1) x0 in M.( for i = 0 to Int.(n-1) do let ti = t0 +. float i *. h and xi = xt.(i) in let ti5 = ti +. 0.5 *. h in let k1 = h * f ti xi in let k2 = h * f ti5 (xi + 0.5 * k1) in let k3 = h * f ti5 (xi + 0.5 * k2) in let k4 = h * f (ti +. h) (xi + k3) in xt.(Int.(i + 1)) pa_infix The purpose of pa_infix is to be able to say, for any OCaml operator symbol, whether one wants it as prefix, postfix or binary infix. It also allows to set the associativity and precedence of operators. The easier way to use it is to use the concrete syntax. For example to turn <+> into an binary infix operator at the same level as + , you just write in your source code: INFIX ( <+> ) LEVEL ( + ) let ( <+> ) x y = (* definition of the operation *) To turn ++ into a post operator — thus binding stronger than function application — simply use POSTFIX ( ++ ) You can then, for example, define it with let ( ++ ) x = x + 1 and use it: let z = 3 ++ . This extension also allows to define purely alphabetical operators (no digits, no underscore, no prime): INFIX subset LEVEL ( && ) Such an operator is used as the standard OCaml alphabetical operators such as lsl : define it with let ( subset ) = ... and use it with a subset x . Beware that these operators become keywords and can therefore not be used as variable names anymore. If your definitions may be useful in several source files, we recommend you create a syntax extension module instead and use the API.





Getting the development version and contributing This project uses bzr as its source control management. You can download the source and its history with bzr branch http://bzr.ocamlcore.org/pa-do/pa-do/trunk pa-do You can then hack on the code, commiting your modifications, and then submit your changes with bzr bundle > file.patch (and post file.patch to the one of the trackers) or bzr send (and email the bundle to one of the developers). For your convenience, a copy of the history is kept in the SVN repository (it may be updated slightly less frequently than the bzr repository). Here is more information on how to use bzr with OCamlForge.

You can then use it with,... As a more interesting example, here is a generic implementation of a Runge-Kutta scheme of order 4 parametrized by a vector space V (assuming that "" is overloaded as the vector addition and "" denotes the scalar multiplication with the floats first):