There are a variety of programming features in ATS that are primarily designed to provide convienience in programming. In this chapter, I will cover macros, compile-time directives and several forms of overloading.
There are two kinds of macros in ATS: a C-like kind and a LISP-like kind respectively.
The following two declarations bind the identifiers N1 and N2 to the abstract syntax trees (not strings) representing 1024 and N1 + N1, respectively:
Suppose we have the following value declaration appearing in the scope of the above macro delarations:
Then N1 * N2 first expands into 1024 * (N1 + N1), which further expands into 1024 * (1024 + 1024). Note that if this example is done in C, then N1 * N2 expands into 1024 * 1024 + 1024, which is different from what we have here. Also note that it makes no difference if we reverse the order of the previous macro definitions:
If we now introduce the following declaration:
then the name N3 is bound to the abstract syntax tree of 2048 + N2. In general, an expression of the form %(exp) refers to the abstract syntax tree representing the value of exp.
If we declare a marco as follows:
then an infinite loop is entered (or more precisely, some macro expansion depth is to be reached) when the identifier LOOP is expanded. There is currently no guard against infinite macro expansion in ATS, and the propgrammer is fully responsible for avoiding it.
There are two forms of LISP-like macros in ATS: short form and long form. These (untyped) macros are highly flexible and expressive, and they can certainly be used in convoluted manners that should probably be avoided in the first place. Some commonly used macro definitions can be found on-line. In order to use LISP-like macros in ATS effectively, the programmer may want to study some examples in LISP involving backquote-comma-notation.
As a macro in short form can simply be considered a special kind of macro in long form, we first give some explanantion on the latter. A macro definition in long form is introduced through the use of the keyword macrodef. For instance, the following syntax introduces a macro name one that refers to some code, that is, abstract syntax tree (AST) representing the integer number 1.
The special syntax `(exp), where no space is allowed between the backquote (`) and the left parenthsis ((), means to form an abstract syntax tree representing the expression written inside the parentheses. This is often referred to as backquote-notation. Intuitively, one may think that a backquote-notation exerts an effect that freezes everything inside it. Let us now define another macro as follows:
The defined macro name one_plus_one refers to some code (i.e., AST) representing 1 + 1. At this point, it is important to stress that the code representing 1 + 1 is different from the code representing 2. The macro name one_plus_one can also be defined as follows:
The syntax ,(exp), where no space is allowed between the comma (,) and the left parenthesis ((), indicates the need to expand (or evaluate) the expression written inside the parentheses. This is often referred to as comma-notation, which is only allowed inside a backquote-notation. Intuitively, a comma-notation cancels out the freezing effect of the enclosing backquote-notation.
In addition to macro names, we can also define macro functions. For instance, the following syntax introduces a macro function cube_mac:
Here are some examples that make use of cube_mac:
fun cubesum (i:int, j: int): int = ,(cube_mac `(i)) + ,(cube_mac `(j)) fun volOfSphere (r: double): double = 4.0 * 3.1416 * ,(cube_mac `(r)) / 3
After macro expansion, the definitions of the functions cubesum and volOfSphere can be written as
The previous macro function cube_mac can also be defined as follows:
The keyword macdef introduces a macro definition in short form. The previous examples that make use of cube_mac can now be written as follows:
fun cubesum (i:int, j: int): int = cube_mac (i) + cube_mac (j) fun volOfSphere (r: double): double = 4.0 * 3.1416 * cube_mac (r) / 3
In terms of syntax, a macro function in short form is just like an ordinary function. In general, if a unary macro function fmac in short form is defined as as follows:
where exp stands for some expression, then one may essentially think that a macro definition in long form is defined as follows:
and each occurrence of fmac(arg) for some expresson arg is automatically rewritten into ,(lfmac(`(arg))). Note that macro functions in short form with multiple arguments are handled analogously.
The primary purpose for introducing macros in short form is to provide a form of syntax that seems more accessible. While macros in long form can be defined recursively (as is to be explained later), macros in short form cannot.