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.
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.
This module allows to locally overload operators and functions.
X is a module
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)and this will be transformed into the appropriate
Int32function calls (the string will be checked to represent a valid
Big_intat compile time). The standard OCaml numeric modules
Complex(provided by the
Ratio(in a separate extension pa_do_num because they require the
nums.cmalibrary (ocamlfind package
num) to be loaded in camlp4 for static checking) are overloaded, as well as two "phantom" modules
Complexmodule use the name
Ito denote the imaginary unit so one can write
Complex.(log(z + 2I) = I / u)
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
div and the unary
neg. To be able to write expressions
using the usual arithmetic operators, like
X.(x * y +
z), simply put
OVERLOAD_ARITHMETIC Xat the beginning of your source file. As another example, let us use the
Ropelibrary. To make it more like strings, one can declare
OVERLOAD_STRING Rope(of_string) OVERLOAD_COMPARISON Rope(compare) OVERLOAD Rope((^) -> concat2) OVERLOAD_STRING_GET Rope(get)With this one can just write
Rope.(if max s u <= "pre" ^ u then s. else u.). Finally, for a polynomial module
Polyon the field of the reals, one probably would like to use (the name of the functions have been chosen to be self-documenting):
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)Then one can use the natural looking
let p = Poly.(1 + `x * `x + `x * `y + `x**k)to declare the polynomial
y. Note that the library caches the evaluation of constants so there will actually be only a single
var_of_string "x"for the entire source file. To support
2has not to be transformed into a polynomial, one has to add a new rule with the
constantsfunction 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.
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
As a simple example of what macros enable you to do, here is a generic "incrementor":
DEFINE INCR(M,x) = M.(x := !x + 1)You can then use it with
INCR(Float, x),... 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):
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)) <- xi + onesixth * (k1 + k4) + onethird * (k2 + k3) done); xt
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 + 1and 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-doYou can then hack on the code, commiting your modifications, and then submit your changes with
bzr bundle > file.patch(and post
file.patchto 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.