Module Macro


module Macro: sig .. end
(sub-module of Pa_do) Improved version of macros that play well with overloading. You can use the following:

At toplevel (structure item):

    DEFINE <uident>
    DEFINE <uident> = <expression>
    DEFINE <uident> (<parameters>) = <expression>
    IFDEF <uident> THEN <structure_items> [ELSE <structure_items>] (END|ENDIF)
    IFNDEF <uident> THEN <structure_items> [ELSE <structure_items>] (END|ENDIF)
    INCLUDE <string>
    

In expressions:

    IFDEF <uident> THEN <expression> [ ELSE <expression> ] (END | ENDIF)
    IFNDEF <uident> THEN <expression> [ ELSE <expression> ] (END | ENDIF)
    DEFINE <lident> = <expression> in <expression> (* in discussion *)
    INCLUDE_AS_STRING <string> (* turn the file content into a string *)
    __FILE__
    __LOCATION__
    

Macros can take parameters starting with a lowercase -- to to be substituted by expressions -- and parameters starting with an uppercase -- to be substituted by module names. The latter allows, for example, to delay overloadings until macro application. Thus you can write:

    DEFINE F(M, x) = let y = M.(1 + x) in M.float y
    
and then use is as F(Int64, 1L). You can also use this feature to parametrise code to be instantiated with different modules (i.e. use it as a poor man defunctorizer): for example, after after DEFINE F(M, y) = M.add (x : M.t) y, F(Int32, 1l) becomes Int32.add (x : Int32.t) 1l.

Macros are lexically scoped: previously defined macros will be expanded in the body of the current macro definition but only the macro parameters will be substituted at the point of use of the macro (also free variables in the body of the macro will be resolved at the point of use of the macro). If you want to change what the body of a macro does and passing a function as a macro parameter is not enough, you can pass another macro as a parameter by using the following syntax :

    DEFINE F(..., X(),...) = ... X(a1,...,aN) ...
    DEFINE G(b1,...,bN) = ...
    ... F(..., G,...) ... (* use F passing the macro G as a parameter *)
    
The interesting point is that G may not use some of its arguments (which will therefore not be evaluated), so it is possible to setup a macro for a general computation and perform only parts of it if needed through a good choice of the macro passed as parameter.

The toplevel statement INCLUDE <string> can be used to include a file containing macro definitions and also any other toplevel items. The included files are looked up in directories passed in via the -I option, falling back to the current directory.

Strong points of Macro:

Altough Macro does not provide type arguments (e.g. for type annotations), it is easy to work around this limitation by using the parametrisation by a module.

exception Invalid of string

type param =
| Lid of string (*Lowercase parameters to be substituted by expressions.*)
| Uid of string (*Capitalized parameters to be substituted by module longidents.*)
| Macro of string (*Uppercase macros names to be expanded in the current macro body.*)
| Unused (*Unused parameter. Interesting for example for macros with several definitions selected defined conditionally which may not use all their parameters.*)
Types of parameters accepted by macros.
val define : ?expr:param list * Camlp4.PreCast.Syntax.Ast.expr -> string -> unit
define name defines the new macro name which has no body. define name ~expr:(params, body) defines the new macro name with the formal parameters params and the body expr.
Raises Invalid if name is not made of uppercase letters.
val undef : string -> unit
undef name undefines the macro name.
Raises Invalid if name is not made of uppercase letters.
val is_defined : string -> bool
is_defined name returns true iff the macro name is defined.
Raises Invalid if name is not made of uppercase letters.
module Hook: sig .. end
Various callbacks for syntax extensions to cooperate nicely with macros.

Utilities


exception Invalid_identifier of string
val is_lowercase_identifier : string -> bool
is_lowercase_identifier lid returns true if the string lid is a lowercase identifier.
val is_capitalized_identifier : string -> bool
Returns true if the string is a capitalized (i.e. module) identifier.
val is_uppercase_identifier : string -> bool
Returns true if the string is an uppercase (i.e. macro) identifier.
module Module_longident: sig .. end
Management of module longidents (aka module paths).